using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Primitives; using ServiceShared.Models.Response.Exception; using ServiceShared; using ServiceShared.Crypto; using System.Text; using ServiceShared.Https; using ServiceOutside.Controllers; using ServiceShared.Models.Response; using ServiceShared.Database; namespace ServiceOutside.Filter { public class TrustedHeader : ActionFilterAttribute, IAsyncResultFilter, IAsyncAuthorizationFilter { /// /// If log type is debug, request headers will be printed out in the console /// private static Log.Types _LogType = Log.Types.INFO; /// /// DbContext to check maintenance state of service /// private static DbContext _DbContext = null; /// /// Sets the log type /// /// Log.Types public static void SetLog(Log.Types logType) { _LogType = logType; } public static void SetDbContext(DbContext dbContext) { _DbContext = dbContext; } /// /// Validates request header /// /// Current context /// public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context) { if (context.HttpContext == null || context.HttpContext.Request == null || context.HttpContext.Request.Headers == null || context.HttpContext.Request.Body == null || context.HttpContext.Request.ContentLength <= 0 || !context.HttpContext.Request.Headers.ContainsKey("User-Agent") || !context.HttpContext.Request.Headers.ContainsKey("Content-Type") || !context.HttpContext.Request.Headers.ContainsKey("Client-Hash") || !context.HttpContext.Request.Headers.ContainsKey("Client-Key") || !context.HttpContext.Request.IsHttps) { context.Result = new JsonResult(new InvalidClientException()); return; } // User Agent validator StringValues userAgent = new StringValues(); if (!context.HttpContext.Request.Headers.TryGetValue("User-Agent", out userAgent) || !Request.ValidUserAgent(userAgent)) { context.Result = new JsonResult(new InvalidClientException()); return; } // Content Type validator StringValues contentType = new StringValues(); if (!context.HttpContext.Request.Headers.TryGetValue("Content-Type", out contentType) || contentType.ToString() != "application/json") { context.Result = new JsonResult(new InvalidClientException()); return; } // Client hash validator StringValues clientHash = new StringValues(); if (!context.HttpContext.Request.Headers.TryGetValue("Client-Hash", out clientHash) || !Request.ValidHeaderHash(clientHash.ToString())) { context.Result = new JsonResult(new InvalidClientException()); return; } // Client Public Key validator StringValues clientKey = new StringValues(); if (!context.HttpContext.Request.Headers.TryGetValue("Client-Key", out clientKey)) { context.Result = new JsonResult(new InvalidClientException()); return; } if (_DbContext != null && _DbContext.GetMaintenance()) { context.HttpContext.Response.StatusCode = 503; context.Result = new JsonResult(new MaintenanceException()); return; } try { // check if valid base64 encoded byte[] publicKey = Convert.FromBase64String(clientKey.ToString()); } catch { context.Result = new JsonResult(new InvalidClientException()); } // Check if client signature in headers if (context.RouteData != null && context.RouteData.Values != null && context.RouteData.Values.ContainsKey("controller")) { string controllerName = (string)context.RouteData.Values["controller"]; if(!string.IsNullOrEmpty(controllerName) && controllerName.ToLower() != "exchange") { if(!context.HttpContext.Request.Headers.ContainsKey("Client-Signature") || !context.HttpContext.Request.Headers.ContainsKey("Client-Signature-Key")) { context.Result = new JsonResult(new InvalidClientException()); return; } } } } /// /// Logs request in debug mode /// /// /// /// public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { if(_LogType == Log.Types.DEBUG) { string headers = ""; foreach (var header in context.HttpContext.Request.Headers) { headers += header.Key + "=" + header.Value + "\r\n"; } string? message = ""; if (context != null && context.HttpContext != null && context.HttpContext.Connection != null && context.HttpContext.Connection.RemoteIpAddress != null) { message = context.HttpContext.Connection.RemoteIpAddress.ToString() + " " + context.ActionDescriptor.DisplayName + "\r\nHeaders:" + headers; } Log.Debug(message); } await next(); } /// /// Adds Trusted Headers to the response /// /// public override void OnActionExecuted(ActionExecutedContext context) { base.OnActionExecuted(context); if (context != null && context.HttpContext != null && context.HttpContext.Response != null && context.HttpContext.Response.Headers != null && context.Controller != null) { BaseController controller = (BaseController)context.Controller; string signatureData = null; if(context.Result != null) { JsonResult jsonResult = (JsonResult)context.Result; if(jsonResult != null && jsonResult.Value != null && jsonResult.Value.GetType() == typeof(EncryptedResponse)) { EncryptedResponse encryptedResponse = ((EncryptedResponse)jsonResult.Value); if(!string.IsNullOrEmpty(encryptedResponse.encrypted_content)) { signatureData = encryptedResponse.encrypted_content; } else { signatureData = encryptedResponse.descriptor; } } } // User Agent if(!context.HttpContext.Response.Headers.ContainsKey("User-Agent")) { context.HttpContext.Response.Headers.Add("User-Agent", "BefundAppServer"); } else { context.HttpContext.Response.Headers["User-Agent"] = "BefundAppServer"; } // Content Type if (!context.HttpContext.Response.Headers.ContainsKey("Content-Type")) { context.HttpContext.Response.Headers.Add("Content-Type", "application/json"); } else { context.HttpContext.Response.Headers["Content-Type"] = "application/json"; } // Server Hash if (!context.HttpContext.Response.Headers.ContainsKey("Server-Hash")) { context.HttpContext.Response.Headers.Add("Server-Hash", Request.GeServerHash()); } else { context.HttpContext.Response.Headers["Server-Hash"] = Request.GeServerHash(); } // Server Key if (!context.HttpContext.Response.Headers.ContainsKey("Server-Key")) { context.HttpContext.Response.Headers.Add("Server-Key", controller.GetServerPublicKey()); } else { context.HttpContext.Response.Headers["Server-Key"] = controller.GetServerPublicKey(); } if(!string.IsNullOrEmpty(signatureData)) { // Server Signature if (!context.HttpContext.Response.Headers.ContainsKey("Server-Signature")) { context.HttpContext.Response.Headers.Add("Server-Signature", controller.GetServerSignature(signatureData)); } else { context.HttpContext.Response.Headers["Server-Signature"] = controller.GetServerSignature(signatureData); } // Server Signature Key if (!context.HttpContext.Response.Headers.ContainsKey("Server-Signature-Key")) { context.HttpContext.Response.Headers.Add("Server-Signature-Key", controller.GetServerSignatureKey()); } else { context.HttpContext.Response.Headers["Server-Signature-Key"] = controller.GetServerSignatureKey(); } } } } } }