This commit is contained in:
2022-12-16 06:01:23 +00:00
parent 26c2ae5cc9
commit effd96143f
310 changed files with 48715 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
namespace Sockeye.Api.ControllerHelpers
{
public class ApiCreatedResponse
{
public object Data { get; }
public ApiCreatedResponse(object result)
{
Data = result;
}
}//eoc
}//eons

View File

@@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Http;
using System;
using System.Net;
using System.Net.Http;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Microsoft.Extensions.Logging;
using Sockeye.Util;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
/// <summary>
/// This is essentially an unhandled exception handler
/// </summary>
public class ApiCustomExceptionFilter : IExceptionFilter
{
private readonly ILogger log;
// public ApiCustomExceptionFilter(ILoggerFactory logger)
// {
// if (logger == null)
// {
// throw new ArgumentNullException(nameof(logger));
// }
// this.log = logger.CreateLogger("Server Exception");
// }
public ApiCustomExceptionFilter(ILogger logger)
{
if (logger == null)
{
throw new ArgumentNullException(nameof(logger));
}
this.log = logger;
}
public void OnException(ExceptionContext context)
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
String message = String.Empty;
#region If need to refine this further and deal with specific types
// var exceptionType = context.Exception.GetType();
// if (exceptionType == typeof(UnauthorizedAccessException))
// {
// message = "Unauthorized Access";
// status = HttpStatusCode.Unauthorized;
// }
// else if (exceptionType == typeof(NotImplementedException))
// {
// message = "A server error occurred.";
// status = HttpStatusCode.NotImplemented;
// }
// // else if (exceptionType == typeof(MyAppException))
// // {
// // message = context.Exception.ToString();
// // status = HttpStatusCode.InternalServerError;
// // }
// else
// {
#endregion
message = context.Exception.Message;
status = HttpStatusCode.InternalServerError;
//}
//No need to log test exceptions to check and filter out
bool loggableError = true;
if (message.StartsWith("Test exception"))
loggableError = false;
//LOG IT
if (loggableError)
log.LogError(context.Exception, "Error");
//Notify ops notification issue
NotifyEventHelper.AddOpsProblemEvent("Server API internal error, see log for more details", context.Exception).Forget();//.Wait();
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json; charset=utf-8";
//This line is critical, without it the response is not proper and fails in various clients (postman, xunit tests with httpclient)
context.ExceptionHandled = true;
response.WriteAsync(JsonConvert.SerializeObject(
new ApiErrorResponse(ApiErrorCode.API_SERVER_ERROR, "generalerror", "Server internal error; see server log for details"),
new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}
));
}
}//eoc
}//eons

View File

@@ -0,0 +1,30 @@
using Newtonsoft.Json;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
/// <summary>
/// Detail error for inner part of error response
/// </summary>
public class ApiDetailError
{
/* WAIT, why does this have CODE AND Error??! */
// [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
// public string Code { get; internal set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; internal set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Target { get; internal set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Error { get; internal set; }
}//eoc
}//eons

View File

@@ -0,0 +1,37 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
/// <summary>
///
/// </summary>
public class ApiError
{
public string Code { get; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public List<ApiDetailError> Details { get; internal set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Message { get; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Target { get; }
public ApiError(ApiErrorCode apiCode, string message = null, string target = null)
{
Code = ((int)apiCode).ToString();
Target = target;
Message=message;
}
}//eoc
}//eons

View File

@@ -0,0 +1,122 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
public class ApiErrorResponse
{
[JsonIgnore]
private ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger<ApiErrorResponse>();
//Mandatory properties
public ApiError Error { get; }
//Generic error
public ApiErrorResponse(ApiErrorCode apiCode, string target = null, string message = null)
{
//try to get a stock message if nothing specified
if (message == null)
{
message = ApiErrorCodeStockMessage.GetTranslationCodeForApiErrorCode(apiCode);
}
Error = new ApiError(apiCode, message, target);
log.LogDebug("apiCode={0}, target={1}, message={2}", apiCode, target, message);
}
//Bad request (MODELSTATE ISSUE) error response handling
public ApiErrorResponse(ModelStateDictionary modelState)
{
if (modelState.IsValid)
{
throw new ArgumentException("ModelState must be invalid", nameof(modelState));
}
//Set outer error and then put validation in details
Error = new ApiError(ApiErrorCode.VALIDATION_FAILED, ApiErrorCodeStockMessage.GetTranslationCodeForApiErrorCode(ApiErrorCode.VALIDATION_FAILED));
//https://www.jerriepelser.com/blog/validation-response-aspnet-core-webapi/
//Message = "Validation Failed";
Error.Details = new List<ApiDetailError>();
foreach (var key in modelState.Keys)
{
var vErrors = modelState[key].Errors;
foreach (ModelError m in vErrors)
{
string msg = "";
if (!string.IsNullOrWhiteSpace(m.ErrorMessage))
{
msg += m.ErrorMessage + ". ";
}
if (m.Exception != null && !string.IsNullOrWhiteSpace(m.Exception.Message))
{
msg += "Exception: " + m.Exception.Message;
}
//example this produces
//
Error.Details.Add(new ApiDetailError() { Target = key, Message = msg, Error = ((int)ApiErrorCode.VALIDATION_INVALID_VALUE).ToString() });
}
}
log.LogDebug("BadRequest - Validation error");
}
//Business rule validation error response
public ApiErrorResponse(List<ValidationError> errors)
{
Error = new ApiError(ApiErrorCode.VALIDATION_FAILED, ApiErrorCodeStockMessage.GetTranslationCodeForApiErrorCode(ApiErrorCode.VALIDATION_FAILED));
Error.Details = new List<ApiDetailError>();
foreach (ValidationError v in errors)
{
Error.Details.Add(new ApiDetailError() { Target = v.Target, Message = v.Message, Error = ((int)v.Code).ToString() });
}
log.LogDebug("BadRequest - Validation error");
}
// public void AddDetailError(ApiErrorCode apiCode, string target = null, string message = null)
// {
// if (Error.Details == null)
// {
// Error.Details = new List<ApiDetailError>();
// }
// //try to get a stock message if nothing specified
// if (message == null)
// {
// message = ApiErrorCodeStockMessage.GetMessage(apiCode);
// }
// Error.Details.Add(new ApiDetailError() { Code = ((int)apiCode).ToString(), Target = target, Message = message });
// }
}//eoc
}//eons

View File

@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
public class ApiNotAuthorizedResponse
{
[JsonIgnore]
private ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger<ApiNotAuthorizedResponse>();
//Mandatory properties
public ApiError Error { get; }
//Generic error
public ApiNotAuthorizedResponse()
{
Error = new ApiError(ApiErrorCode.NOT_AUTHORIZED, ApiErrorCodeStockMessage.GetTranslationCodeForApiErrorCode(ApiErrorCode.NOT_AUTHORIZED));
log.LogDebug("ApiErrorCode={0}, message={1}", (int)ApiErrorCode.NOT_AUTHORIZED, Error.Message);
}
}//eoc
}//eons

View File

@@ -0,0 +1,10 @@
namespace Sockeye.Api.ControllerHelpers
{
public static class ApiOkResponse
{
public static object Response(object result)
{
return new { Data = result };
}
}//eoc
}//eons

View File

@@ -0,0 +1,220 @@
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
/// <summary>
/// Contains the current status of the server
/// is injected everywhere for routes and others to check
/// </summary>
public class ApiServerState
{
//private ILogger log = Sockeye.Util.ApplicationLogging.CreateLogger<ApiServerState>();
public enum ServerState
{
///<summary>Unknown state, used for parsing</summary>
UNKNOWN = 0,
///<summary>No access for anyone API completely locked down. Not set by user but rather by internal server operations like importing or backup.</summary>
Closed = 1,
///<summary>Access only to API Operations routes. Can be set by Ops user</summary>
OpsOnly = 3,
///<summary>Open for all users (default). Can be set by Ops user</summary>
Open = 4
}
private ServerState _currentState = ServerState.Closed;
private ServerState _priorState = ServerState.Closed;
private string _reason = string.Empty;
private string _priorReason = string.Empty;
private bool SYSTEM_LOCK = false;//really this is a license lock but not called that
public ApiServerState()
{
}
internal void SetSystemLock(string reason)
{
//Lock down the server for license related issue
//Only SuperUser account (id=1) can login or do anything, treats as if server was set to closed even if they change it to open
//only way to reset it is to fetch a valid license
// This is set to locked in TWO ways:
// 1) By CoreJobSweeper *if* the user count is mismatched to the license likely due to Users messing directly with the DB
//trying to circumvent licensing
// 2) By License::InitializeAsync upon finding user count mismatch or expired license key
// and initializeasync is called on boot up or when a new key is downloaded and installed only
//
var msg = $"{reason}\r\nLogin as SuperUser to start evaluation / install license";
SetState(ServerState.OpsOnly, msg);
SYSTEM_LOCK = true;
}
//WARNING: if in future this is used for anything other than a license then it will need to see if locked for another reason before unlocking
//recommend putting a code number in the reason then looking to see if it has the matching code
internal void ClearSystemLock()
{
SYSTEM_LOCK = false;
SetState(ServerState.Open, "");
}
/// <summary>
/// Set the server state
/// </summary>
/// <param name="newState"></param>
/// <param name="reason"></param>
public void SetState(ServerState newState, string reason)
{
//No changes allowed during a system lock
if (SYSTEM_LOCK) return;
_reason = reason;//keep the reason even if the state doesn't change
if (newState == _currentState) return;
//Here we will likely need to trigger a notification to users if the state is going to be shutting down or is shut down
_priorState = _currentState;//keep the prior state so it can be resumed easily
_priorReason = _reason;//keep the original reason
_currentState = newState;
}
/// <summary>
/// Get the current state of the server
/// </summary>
/// <returns></returns>
public ServerState GetState()
{
return _currentState;
}
/// <summary>
/// Get the current reason for the state of the server
/// </summary>
/// <returns></returns>
public string Reason
{
get
{
if (_currentState == ServerState.Open)
{
return "";
}
else
{
return $"\"{_reason}\"";
// if (_currentState == ServerState.Closed)
// return $"{ Sockeye.Biz.TranslationBiz.GetDefaultTranslationAsync("ErrorAPI2000").Result}\r\n{_reason}";
// else //opsonly
// return $"{ Sockeye.Biz.TranslationBiz.GetDefaultTranslationAsync("ErrorAPI2001").Result}\r\n{_reason}";
}
}
}
//get the api error code associated with the server state
public ApiErrorCode ApiErrorCode
{
get
{
switch (_currentState)
{
case ServerState.Open:
throw new System.NotSupportedException("ApiServerState:ApiErrorCode - No error code is associated with server state OPEN");
case ServerState.OpsOnly:
return ApiErrorCode.API_OPS_ONLY;
case ServerState.Closed:
return ApiErrorCode.API_CLOSED;
}
throw new System.NotSupportedException("ApiServerState:ApiErrorCode - No error code is associated with server state UNKNOWN");
}
}
public void SetOpsOnly(string reason)
{
//No changes allowed during a system lock
if (SYSTEM_LOCK) return;
SetState(ServerState.OpsOnly, reason);
}
public void SetClosed(string reason)
{
//No changes allowed during a system lock
if (SYSTEM_LOCK) return;
SetState(ServerState.Closed, reason);
}
public void SetOpen()
{
//No changes allowed during a system lock
if (SYSTEM_LOCK) return;
SetState(ServerState.Open, string.Empty);
}
public void ResumePriorState()
{
//No changes allowed during a system lock
if (SYSTEM_LOCK) return;
SetState(_priorState, _priorReason);
}
public bool IsOpsOnly
{
get
{
return _currentState == ServerState.OpsOnly && !SYSTEM_LOCK;
}
}
public bool IsOpen
{
get
{
return _currentState != ServerState.Closed && _currentState != ServerState.OpsOnly && !SYSTEM_LOCK;
}
}
public bool IsClosed
{
get
{
return _currentState == ServerState.Closed || SYSTEM_LOCK;
}
}
// public bool IsOpenOrOpsOnly
// {
// get
// {
// return (IsOpen || IsOpsOnly) && !SYSTEM_LOCK;
// }
// }
public bool IsSystemLocked
{
get
{
return SYSTEM_LOCK;
}
}
}//eoc
}//eons

View File

@@ -0,0 +1,180 @@
using System;
using System.Threading.Tasks;
using System.IO;
using System.Text;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;
using System.Collections.Generic;
using Sockeye.Models;
using Sockeye.Util;
namespace Sockeye.Api.ControllerHelpers
{
//Adapted from the example found here: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads#uploading-large-files-with-streaming
//See AttachmentController at bottom of class for example of form that works with this code
/// <summary>
/// Handle processing uplod form with potentially huge files being uploaded (which means can't use simplest built in upload handler method)
/// </summary>
internal static class ApiUploadProcessor
{
/// <summary>
/// handle upload
/// </summary>
/// <param name="httpContext"></param>
/// <returns><see cref="ApiUploadedFilesResult"/> list of files and form field data (if present)</returns>
internal static async Task<ApiUploadedFilesResult> ProcessUploadAsync(Microsoft.AspNetCore.Http.HttpContext httpContext)
{
ApiUploadedFilesResult result = new ApiUploadedFilesResult();
FormOptions _defaultFormOptions = new FormOptions();
try
{
// Used to accumulate all the form url encoded key value pairs in the
// request.
var formAccumulator = new KeyValueAccumulator();
var boundary = MultipartRequestHelper.GetBoundary(
MediaTypeHeaderValue.Parse(httpContext.Request.ContentType),
_defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, httpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
while (section != null)
{
ContentDispositionHeaderValue contentDisposition;
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);
if (hasContentDispositionHeader)
{
if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
string filePathAndName = string.Empty;
var CleanedUploadFileName = contentDisposition.FileName.Value.Replace("\"", "");
//get temp file path and temp file name
filePathAndName = FileUtil.NewRandomAttachmentFilesFolderFileName;
//save to disk
using (var stream = new FileStream(filePathAndName, FileMode.Create))
{
await section.Body.CopyToAsync(stream);
}
result.UploadedFiles.Add(new UploadedFileInfo()
{
InitialUploadedPathName = filePathAndName,
OriginalFileName = CleanedUploadFileName,
MimeType = section.ContentType
});
}
else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition))
{
// Content-Disposition: form-data; name="key"
//
// value
// Do not limit the key name length here because the
// multipart headers length limit is already in effect.
var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name);
var encoding = GetEncoding(section);
using (var streamReader = new StreamReader(
section.Body,
encoding,
detectEncodingFromByteOrderMarks: true,
bufferSize: 1024,
leaveOpen: true))
{
// The value length limit is enforced by MultipartBodyLengthLimit
var value = await streamReader.ReadToEndAsync();
if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase))
{
value = String.Empty;
}
formAccumulator.Append(key.Value, value);
if (formAccumulator.ValueCount > _defaultFormOptions.ValueCountLimit)
{
throw new InvalidDataException($"Form key count limit {_defaultFormOptions.ValueCountLimit} exceeded.");
}
}
}
}
// Drains any remaining section body that has not been consumed and
// reads the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
//Get any extra form fields and return them
result.FormFieldData = formAccumulator.GetResults();
return result;
}
catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex)
{
//most commonly here due to file too large
result.Error = $"Code:{ex.StatusCode}, Error: {ex.Message}";
return result;
}
}
public static void DeleteTempUploadFile(ApiUploadedFilesResult uploadFormData)
{
if (uploadFormData.UploadedFiles.Count > 0)
{
foreach (UploadedFileInfo a in uploadFormData.UploadedFiles)
{
System.IO.File.Delete(a.InitialUploadedPathName);
}
}
}
private static Encoding GetEncoding(MultipartSection section)
{
MediaTypeHeaderValue mediaType;
var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType);
// UTF-7 is insecure and should not be honored. UTF-8 will succeed in
// most cases.
if (!hasMediaTypeHeader || Encoding.UTF8.Equals(mediaType.Encoding))
{
return Encoding.UTF8;
}
return mediaType.Encoding;
}
/// <summary>
/// Contains result of upload form processor
/// </summary>
public class ApiUploadedFilesResult
{
public Dictionary<string, Microsoft.Extensions.Primitives.StringValues> FormFieldData { get; set; }
public List<UploadedFileInfo> UploadedFiles { get; set; }
public string Error { get; set; }
public ApiUploadedFilesResult()
{
FormFieldData = new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
UploadedFiles = new List<UploadedFileInfo>();
Error = null;
}
}
}//eoc
}//eons

View File

@@ -0,0 +1,228 @@
using EnumsNET;
using System.Collections.Generic;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
//AUTHORIZATION ROLES: NOTE - this is only 'stage1' of generally checking rights, individual objects can also have business rules that affect access exactly as these roles do
//Most objects won't need more than this but some specialized ones will have further checks depending on biz rules
internal static class Authorized
{
/// <summary>
/// User has any role restricted or full
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="CheckRoles"></param>
/// <returns></returns>
internal static bool HasAnyRole(IDictionary<object, object> HttpContextItems, AuthorizationRoles CheckRoles)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasAnyRole(currentUserRoles, CheckRoles);
}
/// <summary>
/// User has any role restricted or full
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="CheckRoles"></param>
/// <returns></returns>
internal static bool HasAnyRole(AuthorizationRoles currentUserRoles, AuthorizationRoles CheckRoles)
{
if (currentUserRoles.HasAnyFlags(CheckRoles))
return true;
return false;
}
/// <summary>
/// any access at all?
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasAnyRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasAnyRole(currentUserRoles, aType);
}
/// <summary>
/// User has any access at all to this object?
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasAnyRole(AuthorizationRoles currentUserRoles, SockType aType)
{
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
var AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change | RoleSet.Select;
return currentUserRoles.HasAnyFlags(AllowedRoles);
}
/// <summary>
/// READ FULL RECORD (not just name and id)
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasSelectRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasSelectRole(currentUserRoles, aType);
}
/// <summary>
/// SELECT BY NAME
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasSelectRole(AuthorizationRoles currentUserRoles, SockType aType)
{
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
//NOTE: this assumes that if you can change you can read
if (currentUserRoles.HasAnyFlags(RoleSet.Change))
return true;
if (currentUserRoles.HasAnyFlags(RoleSet.ReadFullRecord))
return true;
if (currentUserRoles.HasAnyFlags(RoleSet.Select))
return true;
return false;
}
/// <summary>
/// READ FULL RECORD (not just name and id)
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasReadFullRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasReadFullRole(currentUserRoles, aType);
}
/// <summary>
/// READ FULL RECORD (not just name and id)
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasReadFullRole(AuthorizationRoles currentUserRoles, SockType aType)
{
//NOTE: this assumes that if you can change you can read
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
var AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change;
return currentUserRoles.HasAnyFlags(AllowedRoles);
}
/// <summary>
/// CREATE
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasCreateRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasCreateRole(currentUserRoles, aType);
}
/// <summary>
/// CREATE
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasCreateRole(AuthorizationRoles currentUserRoles, SockType aType)
{
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
if (currentUserRoles.HasAnyFlags(RoleSet.Change))
return true;
return false;
}
/// <summary>
/// MODIFY
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasModifyRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
return HasModifyRole(currentUserRoles, aType);
}
/// <summary>
/// MODIFY
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
internal static bool HasModifyRole(AuthorizationRoles currentUserRoles, SockType aType)
{
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
if (currentUserRoles.HasAnyFlags(RoleSet.Change))
return true;
return false;
}
/// <summary>
/// DELETE
/// </summary>
/// <param name="HttpContextItems"></param>
/// <param name="aType"></param>
/// <returns></returns>
//For now just going to treat as a modify, but for maximum flexibility keeping this as a separate method in case we change our minds in future
internal static bool HasDeleteRole(IDictionary<object, object> HttpContextItems, SockType aType)
{
AuthorizationRoles currentUserRoles = UserRolesFromContext.Roles(HttpContextItems);
long currentUserId = UserIdFromContext.Id(HttpContextItems);
return HasDeleteRole(currentUserRoles, aType);
}
/// <summary>
/// DELETE
/// </summary>
/// <param name="currentUserRoles"></param>
/// <param name="aType"></param>
/// <returns></returns>
//For now just going to treat as a modify, but for maximum flexibility keeping this as a separate method in case we change our minds in future
internal static bool HasDeleteRole(AuthorizationRoles currentUserRoles, SockType aType)
{
var RoleSet = BizRoles.GetRoleSet(aType);
if (RoleSet == null) return false;
if (currentUserRoles.HasAnyFlags(RoleSet.Change))
return true;
return false;
}
}
}//eons

View File

@@ -0,0 +1,50 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Sockeye.Api.ControllerHelpers
{
//FROM DOCS HERE:
//https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads#uploading-large-files-with-streaming
//https://github.com/aspnet/Docs/tree/74a44669d5e7039e2d4d2cb3f8b0c4ed742d1124/aspnetcore/mvc/models/file-uploads/sample/FileUploadSample
/// <summary>
///
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnResourceExecuting(ResourceExecutingContext context)
{
var formValueProviderFactory = context.ValueProviderFactories
.OfType<FormValueProviderFactory>()
.FirstOrDefault();
if (formValueProviderFactory != null)
{
context.ValueProviderFactories.Remove(formValueProviderFactory);
}
var jqueryFormValueProviderFactory = context.ValueProviderFactories
.OfType<JQueryFormValueProviderFactory>()
.FirstOrDefault();
if (jqueryFormValueProviderFactory != null)
{
context.ValueProviderFactories.Remove(jqueryFormValueProviderFactory);
}
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.IO;
using Microsoft.Net.Http.Headers;
namespace Sockeye.Api.ControllerHelpers
{
/// <summary>
///
/// </summary>
public static class MultipartRequestHelper
{
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
// The spec says 70 characters is a reasonable limit.
/// <summary>
///
/// </summary>
/// <param name="contentType"></param>
/// <param name="lengthLimit"></param>
/// <returns></returns>
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
{
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
if (string.IsNullOrWhiteSpace(boundary))
{
throw new InvalidDataException("Missing content-type boundary.");
}
if (boundary.Length > lengthLimit)
{
throw new InvalidDataException(
$"Multipart boundary length limit {lengthLimit} exceeded.");
}
return boundary;
}
/// <summary>
///
/// </summary>
/// <param name="contentType"></param>
/// <returns></returns>
public static bool IsMultipartContentType(string contentType)
{
return !string.IsNullOrEmpty(contentType)
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
}
/// <summary>
///
/// </summary>
/// <param name="contentDisposition"></param>
/// <returns></returns>
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="key";
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
}
/// <summary>
///
/// </summary>
/// <param name="contentDisposition"></param>
/// <returns></returns>
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
{
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
return contentDisposition != null
&& contentDisposition.DispositionType.Equals("form-data")
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Collections.Generic;
namespace Sockeye.Api.ControllerHelpers
{
internal static class UserIdFromContext
{
internal static long Id(IDictionary<object, object> HttpContextItems)
{
long? l = (long?)HttpContextItems["AY_USER_ID"];
if (l==null)
return 0L;
return (long)l;
}
}
}//eons

View File

@@ -0,0 +1,20 @@
using EnumsNET;
using System.Collections.Generic;
namespace Sockeye.Api.ControllerHelpers
{
internal static class UserNameFromContext
{
internal static string Name(IDictionary<object, object> HttpContextItems)
{
string s = (string)HttpContextItems["AY_USERNAME"];
if (string.IsNullOrWhiteSpace(s))
return "UNKNOWN USER NAME";
return s;
}
}
}//eons

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
internal static class UserRolesFromContext
{
internal static AuthorizationRoles Roles(IDictionary<object, object> HttpContextItems)
{
return (AuthorizationRoles)HttpContextItems["AY_ROLES"];
}
}
}//eons

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Sockeye.Api.ControllerHelpers
{
internal static class UserTranslationIdFromContext
{
internal static long Id(IDictionary<object, object> HttpContextItems)
{
long? l = (long?)HttpContextItems["AY_TRANSLATION_ID"];
if (l == null)
return Sockeye.Util.ServerBootConfig.SOCKEYE_DEFAULT_TRANSLATION_ID;
return (long)l;
}
}
}//eons

View File

@@ -0,0 +1,17 @@
using System.Collections.Generic;
using Sockeye.Biz;
namespace Sockeye.Api.ControllerHelpers
{
internal static class UserTypeFromContext
{
internal static UserType Type(IDictionary<object, object> HttpContextItems)
{
return (UserType)HttpContextItems["AY_USER_TYPE"];
}
}
}//eons