138 lines
5.1 KiB
C#
138 lines
5.1 KiB
C#
|
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
|
||
|
{/// <summary>
|
||
|
/// Check username and password are correct or if request can be anonymous
|
||
|
/// </summary>
|
||
|
/// <param name="username">Username, can and will be "" on first request for resource</param>
|
||
|
/// <param name="password">Password, can and will be "" on first request for resource</param>
|
||
|
/// <returns>true for authorized, false for unauthorized</returns>
|
||
|
public delegate bool Authenticate(string username, string password);
|
||
|
/// <summary>
|
||
|
/// Check username and password are correct or if request can be anonymous
|
||
|
/// </summary>
|
||
|
/// <param name="context">Server Context</param>
|
||
|
/// <param name="username">Username, can and will be "" on first request for resource</param>
|
||
|
/// <param name="password">Password, can and will be "" on first request for resource</param>
|
||
|
/// <returns>true for authorized, false for unauthorized</returns>
|
||
|
public delegate bool AuthenticateWithContext(ServerContext context,string username,string password);
|
||
|
/// <summary>
|
||
|
/// Protect server with password
|
||
|
/// </summary>
|
||
|
public class BasicAuthRequestHandler : IRequestHandler
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Construct server for user authorization
|
||
|
/// </summary>
|
||
|
/// <param name="auth">callback for authorization</param>
|
||
|
/// <param name="inner">server to protect</param>
|
||
|
/// <param name="realm">realm parameter in WWW-Auhenticate Header</param>
|
||
|
public BasicAuthRequestHandler(Authenticate auth,IRequestHandler inner,string realm="SampleRealm")
|
||
|
{
|
||
|
Authenticate = auth;
|
||
|
InnerServer = inner;
|
||
|
Realm = realm;
|
||
|
}
|
||
|
/// <summary>
|
||
|
/// Construct server for user authorization (With ServerContext in callback)
|
||
|
/// </summary>
|
||
|
/// <param name="auth">callback for authorization</param>
|
||
|
/// <param name="inner">server to protect</param>
|
||
|
/// <param name="realm">realm parameter in WWW-Auhenticate Header</param>
|
||
|
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);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Server to protect
|
||
|
/// </summary>
|
||
|
|
||
|
public IRequestHandler InnerServer { get; set; }
|
||
|
/// <summary>
|
||
|
/// Authentication callback without ServerContext
|
||
|
/// </summary>
|
||
|
public Authenticate Authenticate { get; set; }
|
||
|
/// <summary>
|
||
|
/// Authentication callback with ServerContext
|
||
|
/// </summary>
|
||
|
|
||
|
public AuthenticateWithContext AuthenticateWithContext {get;set;}
|
||
|
/// <summary>
|
||
|
/// Realm parameter in WWW-Authenticate header
|
||
|
/// </summary>
|
||
|
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<bool> 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;
|
||
|
}
|
||
|
}
|
||
|
}
|