Browse Source
Development more works as before. On Production, we store POST requests in tasks-{uid}.json that is only seved to the specific user.mj-deploy
Standa Lukeš
4 years ago
3 changed files with 176 additions and 9 deletions
@ -0,0 +1,142 @@ |
|||||
|
using System; |
||||
|
using System.Net; |
||||
|
using System.Net.Http; |
||||
|
using System.Net.Http.Headers; |
||||
|
using System.Threading.Tasks; |
||||
|
using AngleSharp.Html.Dom; |
||||
|
using AngleSharp.Html.Parser; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
|
||||
|
namespace Ksp.WebServer |
||||
|
{ |
||||
|
public class KspAuthenticator |
||||
|
{ |
||||
|
readonly KspProxyConfig kspProxyConfig; |
||||
|
readonly ILogger<KspAuthenticator> logger; |
||||
|
|
||||
|
// request to https://ksp.mff.cuni.cz/auth/manage.cgi?mode=change
|
||||
|
// contains user information
|
||||
|
|
||||
|
public KspAuthenticator( |
||||
|
IOptions<KspProxyConfig> kspProxyConfig, |
||||
|
ILogger<KspAuthenticator> logger) |
||||
|
{ |
||||
|
this.kspProxyConfig = kspProxyConfig.Value; |
||||
|
this.logger = logger; |
||||
|
} |
||||
|
|
||||
|
public static UnverifiedAuthCookie ParseAuthCookie(string cookie) |
||||
|
{ |
||||
|
var s = cookie.Split(':'); |
||||
|
return new UnverifiedAuthCookie( |
||||
|
UserId.Parse(s[1]), |
||||
|
s[3], |
||||
|
s[2].Split(',') |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
async Task<IHtmlDocument> FetchPage(string url, string authCookie) |
||||
|
{ |
||||
|
Console.WriteLine($"AuthCookie={authCookie}"); |
||||
|
var cookies = new CookieContainer(); |
||||
|
cookies.Add(new Uri(kspProxyConfig.Host), new Cookie("ksp_auth", Uri.EscapeDataString(authCookie))); |
||||
|
var c = new HttpClient(new HttpClientHandler { CookieContainer = cookies }); |
||||
|
var rq = new HttpRequestMessage(HttpMethod.Get, $"{kspProxyConfig.Host}/{url}"); |
||||
|
rq.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html")); |
||||
|
rq.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xhtml+xml")); |
||||
|
if (!string.IsNullOrEmpty(kspProxyConfig.Authorization)) |
||||
|
rq.Headers.Authorization = |
||||
|
AuthenticationHeaderValue.Parse(kspProxyConfig.Authorization); |
||||
|
var rs = await c.SendAsync(rq); |
||||
|
logger.LogInformation("Verification request for response {code}", rs.StatusCode); |
||||
|
if (rs.StatusCode != HttpStatusCode.OK) |
||||
|
return null; |
||||
|
var response = await rs.Content.ReadAsStringAsync(); |
||||
|
var parser = new HtmlParser(); |
||||
|
return parser.ParseDocument(response); |
||||
|
} |
||||
|
|
||||
|
public async Task<VerifiedUserInfo> VerifyUser(string cookie) |
||||
|
{ |
||||
|
var parsedCookie = ParseAuthCookie(cookie); |
||||
|
logger.LogInformation("Verifying user {uid}", parsedCookie.Id); |
||||
|
var page = await FetchPage("auth/manage.cgi?mode=change", cookie); |
||||
|
var form = page?.QuerySelector("#content form"); |
||||
|
var email = (form?.QuerySelector("input#email") as IHtmlInputElement)?.Value; |
||||
|
var userName = (form?.QuerySelector("input#logname") as IHtmlInputElement)?.Value; |
||||
|
var showName = (form?.QuerySelector("input#showname") as IHtmlInputElement)?.Value; |
||||
|
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(showName)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
return new VerifiedUserInfo( |
||||
|
parsedCookie.Id, |
||||
|
showName, |
||||
|
parsedCookie.Roles, |
||||
|
email, |
||||
|
userName |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class UserId |
||||
|
{ |
||||
|
public int Value { get; } |
||||
|
public UserId(int value) |
||||
|
{ |
||||
|
this.Value = value; |
||||
|
} |
||||
|
|
||||
|
public override bool Equals(object obj) |
||||
|
{ |
||||
|
return obj is UserId id && |
||||
|
Value == id.Value; |
||||
|
} |
||||
|
|
||||
|
public override int GetHashCode() |
||||
|
{ |
||||
|
return HashCode.Combine(Value); |
||||
|
} |
||||
|
|
||||
|
public override string ToString() => $"uid[{Value}]"; |
||||
|
public static UserId Parse(string val) |
||||
|
{ |
||||
|
var id = int.Parse(val); |
||||
|
if (id <= 0) throw new Exception(); |
||||
|
return new UserId(id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Cookie, for example 1603380331:1662:auth_master,org:Standa=20Luke=c5=a1:SIGNATURE
|
||||
|
public sealed class UnverifiedAuthCookie |
||||
|
{ |
||||
|
public UserId Id { get; } |
||||
|
public string FullName { get; } |
||||
|
public string[] Roles { get; } |
||||
|
public UnverifiedAuthCookie(UserId id, string fullName, string[] roles) |
||||
|
{ |
||||
|
this.Id = id; |
||||
|
this.FullName = fullName; |
||||
|
this.Roles = roles; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public sealed class VerifiedUserInfo |
||||
|
{ |
||||
|
public UserId Id { get; } |
||||
|
public string FullName { get; } |
||||
|
public string[] Roles { get; } |
||||
|
public string Email { get; } |
||||
|
public string UserName { get; } |
||||
|
|
||||
|
public VerifiedUserInfo(UserId id, string fullName, string[] roles, string email, string userName) |
||||
|
{ |
||||
|
this.Id = id; |
||||
|
this.FullName = fullName; |
||||
|
this.Roles = roles; |
||||
|
this.Email = email; |
||||
|
this.UserName = userName; |
||||
|
} |
||||
|
} |
||||
|
} |
Reference in new issue