first commit
This commit is contained in:
239
F4SDwebService/Authentication.cs
Normal file
239
F4SDwebService/Authentication.cs
Normal file
@@ -0,0 +1,239 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using C4IT.Security;
|
||||
using C4IT.Logging;
|
||||
using C4IT.XML;
|
||||
using C4IT.FASD.Base;
|
||||
using C4IT.DataHistoryProvider;
|
||||
using static C4IT.Logging.cLogManager;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Reflection;
|
||||
using Microsoft.Ajax.Utilities;
|
||||
|
||||
namespace F4SDwebService
|
||||
{
|
||||
public class cAuthentication : DelegatingHandler
|
||||
{
|
||||
private const string WwwAuthenticateHeader = "WWW-Authenticate";
|
||||
private const string Basic = "Basic";
|
||||
|
||||
public cDataHistoryCollector Collector = null;
|
||||
public Dictionary<string, cOneTimePW> OTPWs = null;
|
||||
|
||||
protected override async Task<HttpResponseMessage> SendAsync(
|
||||
HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Collector == null || OTPWs == null)
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
|
||||
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
||||
try
|
||||
{
|
||||
|
||||
var Path = request.RequestUri.LocalPath.ToLowerInvariant();
|
||||
|
||||
var UserInfo = ValidateBearer(request);
|
||||
|
||||
if (UserInfo != null)
|
||||
{
|
||||
request.Properties.Add("F4SD_UserInfo", UserInfo);
|
||||
return await base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
|
||||
if (!cLogManager.DefaultLogger.IsDebug
|
||||
&& !Path.EndsWith("checkconnection")
|
||||
&& !Path.EndsWith("logon/logon")
|
||||
)
|
||||
{
|
||||
var response = ValidateOTP(request);
|
||||
if (response != null)
|
||||
{
|
||||
var tsc = new TaskCompletionSource<HttpResponseMessage>();
|
||||
tsc.SetResult(response);
|
||||
return await tsc.Task;
|
||||
}
|
||||
}
|
||||
var _res = await base.SendAsync(request, cancellationToken);
|
||||
return _res;
|
||||
}
|
||||
catch (System.OperationCanceledException EC)
|
||||
{
|
||||
LogEntry($"The operation of the request '{request.RequestUri} was canceled.'", LogLevels.Debug);
|
||||
LogException(EC, LogLevels.Debug);
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (CM != null) LogMethodEnd(CM);
|
||||
}
|
||||
|
||||
var responseErr = new HttpResponseMessage(HttpStatusCode.InternalServerError);
|
||||
var tsc2 = new TaskCompletionSource<HttpResponseMessage>();
|
||||
tsc2.SetResult(responseErr);
|
||||
return await tsc2.Task;
|
||||
}
|
||||
|
||||
private cF4sdUserInfo ValidateBearer(HttpRequestMessage request)
|
||||
{
|
||||
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
||||
try
|
||||
{
|
||||
if (Collector == null)
|
||||
return null;
|
||||
|
||||
var authenticationHeaderValue = request.Headers.Authorization;
|
||||
if (authenticationHeaderValue == null || string.IsNullOrEmpty(authenticationHeaderValue.Scheme) || !authenticationHeaderValue.Scheme.Equals("Bearer") || string.IsNullOrEmpty(authenticationHeaderValue.Parameter))
|
||||
{
|
||||
LogEntry($"no valid bearer authentication header", LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
var _res = Collector.JwtTokenHandler.ValidateToken(authenticationHeaderValue.Parameter, Collector.JwtTokenValidationParameters, out var _res2);
|
||||
|
||||
if (!(_res?.Identity?.IsAuthenticated == true))
|
||||
{
|
||||
LogEntry($"bearer token could not validated", LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!(_res2 is JwtSecurityToken _sec))
|
||||
{
|
||||
LogEntry($"bearer token is not a JwtSecurityToken", LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
var _now = DateTime.UtcNow;
|
||||
if (_sec.ValidFrom > _now || _sec.ValidTo < _now)
|
||||
{
|
||||
LogEntry($"bearer token could is not a JwtSecurityToken", LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
var strNameId = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.NameId)?.Value;
|
||||
if (!Guid.TryParse(strNameId, out var _nameId))
|
||||
{
|
||||
LogEntry($"bearer token has no valid NameId property", LogLevels.Debug);
|
||||
return null;
|
||||
}
|
||||
|
||||
var _retVal = new cF4sdUserInfo() { Id = _nameId, ValidUntil = _sec.ValidTo, LogonTime = _sec.ValidFrom };
|
||||
|
||||
_retVal.Name = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.UniqueName).Value;
|
||||
var strAuthTime = _sec.Claims.First(c => c.Type == JwtRegisteredClaimNames.AuthTime).Value;
|
||||
if (DateTime.TryParseExact(strAuthTime, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff'z'", null, System.Globalization.DateTimeStyles.AssumeUniversal, out var _AuthTime))
|
||||
_retVal.LogonTime = _AuthTime;
|
||||
|
||||
var _strLoginMethod = _sec.Claims.First(c => c.Type == "login_method").Value;
|
||||
_retVal.AccountType = cXmlParser.GetEnumFromString<cF4sdUserInfo.enumAccountType>(_strLoginMethod, cF4sdUserInfo.enumAccountType.unknown);
|
||||
|
||||
_retVal.AdAccount = _sec.Claims.First(c => c.Type == "login_account").Value;
|
||||
_retVal.AdSid = _sec.Claims.First(c => c.Type == "account_sid").Value;
|
||||
_retVal.Language = _sec.Claims.First(c => c.Type == "language").Value;
|
||||
|
||||
var _roles = _sec.Claims.Where(c => c.Type == "roles")?.Select(c => c.Value)?.ToList();
|
||||
if (_roles?.Count() > 0)
|
||||
_retVal.Roles = _roles;
|
||||
|
||||
if (cLogManager.DefaultLogger.IsDebug)
|
||||
{
|
||||
var _str = JsonConvert.SerializeObject(_retVal, Formatting.Indented);
|
||||
var _lst = new List<string>(2) { "Bearer user info:", _str };
|
||||
cLogManager.DefaultLogger.LogList(LogLevels.Debug, _lst);
|
||||
}
|
||||
return _retVal;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (CM != null) LogMethodEnd(CM);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private HttpResponseMessage ValidateOTP(HttpRequestMessage request)
|
||||
{
|
||||
bool auth = false;
|
||||
try
|
||||
{
|
||||
var authenticationHeaderValue = request.Headers.Authorization;
|
||||
|
||||
if (authenticationHeaderValue == null || string.IsNullOrEmpty(authenticationHeaderValue.Scheme) || !authenticationHeaderValue.Scheme.Equals(Basic) || string.IsNullOrEmpty(authenticationHeaderValue.Parameter))
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||
response.Headers.Add(WwwAuthenticateHeader, Basic);
|
||||
return response;
|
||||
}
|
||||
|
||||
string authValue = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authenticationHeaderValue.Parameter));
|
||||
|
||||
string[] authParts = authValue.Split(':');
|
||||
|
||||
if (authParts.Length == 2)
|
||||
{
|
||||
if (OTPWs == null)
|
||||
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, "no OTP providers configured.");
|
||||
cOneTimePW OTP;
|
||||
if ((OTPWs != null) && OTPWs.TryGetValue(authParts[0], out OTP))
|
||||
{
|
||||
UInt32 Id;
|
||||
if (UInt32.TryParse(authParts[1], System.Globalization.NumberStyles.Integer, null, out Id))
|
||||
{
|
||||
auth = OTP.Check(Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("Key contains {0} items!", authParts.Length));
|
||||
}
|
||||
catch { }
|
||||
|
||||
if (!auth)
|
||||
{
|
||||
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||
response.Headers.Add(WwwAuthenticateHeader, Basic);
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static public cF4sdUserInfo GetUserInfo(HttpActionContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var _props = context?.Request?.Properties;
|
||||
if (_props == null)
|
||||
return null;
|
||||
|
||||
if (!_props.TryGetValue("F4SD_UserInfo", out var _objUI))
|
||||
return null;
|
||||
|
||||
if (_objUI is cF4sdUserInfo userInfo)
|
||||
return userInfo;
|
||||
}
|
||||
catch (Exception E)
|
||||
{
|
||||
LogException(E);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user