using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System.Security.Claims; namespace zero.Context; public class ZeroContext : IZeroContext { /// public Application Application { get; protected set; } /// public string AppId { get; protected set; } /// public ClaimsPrincipal BackofficeUser { get; protected set; } /// public bool IsBackofficeRequest { get; protected set; } /// public bool IsLoggedIntoBackoffice { get; protected set; } /// public IZeroOptions Options { get; protected set; } /// public Route Route => ResolvedRoute?.Route; /// public IRouteModel ResolvedRoute => HttpContextAccessor?.HttpContext?.Features.Get(); /// public IZeroStore Store { get; private set; } /// public IServiceProvider Services { get; private set; } /// public ZeroContextScope Scope { get; private set; } protected IApplicationResolver AppResolver { get; private set; } protected ICultureResolver CultureResolver { get; private set; } protected ILogger Logger { get; private set; } protected IHandlerHolder Handler { get; private set; } protected IHttpContextAccessor HttpContextAccessor { get; private set; } protected IPrimitiveTypeCollection ValueCollection { get; private set; } bool _resolved = false; public ZeroContext(IZeroOptions options, IHttpContextAccessor httpContextAccessor, IApplicationResolver appResolver, ICultureResolver cultureResolver, ILogger logger, IZeroStore store, IHandlerHolder handler, IServiceProvider services) { Options = options; AppResolver = appResolver; CultureResolver = cultureResolver; Logger = logger; Store = store; Handler = handler; ValueCollection = new PrimitiveTypeCollection(); HttpContextAccessor = httpContextAccessor; Services = services; } /// public async virtual Task Resolve(HttpContext context) { if (_resolved) { return; } if (context?.Request is null) { Store.ResolvedDatabase = null; return; } //if (!Options.SetupCompleted) //{ // return; //} // TODO setup _resolved = true; // check if the current request is a backoffice request IsBackofficeRequest = context.IsBackofficeRequest(Options.ZeroPath); // get the currently logged-in backoffice user BackofficeUser = new ClaimsPrincipal(); IsLoggedIntoBackoffice = false; AuthenticateResult authResult = await context.AuthenticateAsync(Constants.Auth.BackofficeScheme); if (authResult?.Principal is not null) { BackofficeUser = authResult.Principal; IsLoggedIntoBackoffice = true; } // resolve current application Application = await AppResolver.Resolve(context, BackofficeUser); AppId = Application.Id; Logger.LogDebug("Resolved {appId} ({uri})", AppId, context.Request.Host.ToString() + context.Request.Path.Value.EnsureStartsWith('/')); // set default database for document store Store.ResolvedDatabase = Application.Database; // set current culture await CultureResolver.Resolve(this); // set context scope Scope = new(Store, Store.ResolvedDatabase, Application); } /// public T Get() => ValueCollection.Get(); /// public void Set(T value) => ValueCollection.Set(value); /// public void Remove() => ValueCollection.Remove(); /// public ZeroContextScope CreateScope(Application app) { ApplyScope(app.Database, app); return new ZeroContextScope(Store, app.Database, app, Scope, scope => { Scope = scope.Previous; ApplyScope(Scope.Database, Scope.Application); }); } /// public ZeroContextScope CreateScope(string database) { ApplyScope(database); return new ZeroContextScope(Store, database, null, Scope, scope => { Scope = scope.Previous; ApplyScope(Scope.Database, Scope.Application); }); } /// /// Apply a database scope /// void ApplyScope(string database, Application app = null) { Application = app; AppId = app?.Id; Store.ResolvedDatabase = database; } } public class ZeroContextScope : IDisposable { public ZeroContextScope(IZeroStore store, string database, Application application, ZeroContextScope previous = null, Action onDispose = null) { Store = store; Database = database; Application = application; Previous = previous; _onDispose = onDispose; } public IZeroStore Store { get; set; } public Application Application { get; private set; } public string Database { get; private set; } public ZeroContextScope Previous { get; private set; } Action _onDispose = null; public void Dispose() { _onDispose?.Invoke(this); } } public interface IZeroContext { /// /// Currently loaded application /// Application Application { get; } /// /// Current loaded application Id /// string AppId { get; } /// /// Resolved backoffice user principal /// ClaimsPrincipal BackofficeUser { get; } /// /// Whether the current request is a backoffice request or not /// bool IsBackofficeRequest { get; } /// /// Whether the user is logged into the backoffice /// bool IsLoggedIntoBackoffice { get; } /// /// Global zero options /// IZeroOptions Options { get; } /// /// Document store /// IZeroStore Store { get; } /// /// Service container /// IServiceProvider Services { get; } /// /// Current context scope /// ZeroContextScope Scope { get; } /// /// Matching (frontend) path route /// Route Route { get; } /// /// Matching (frontend) resolved route /// IRouteModel ResolvedRoute { get; } /// /// Resolves the current application (for backoffice + frontend requests) and /// the currently active backoffice user, as users are not signed in with the default scheme and do therefore not populate HttpContext.User /// Task Resolve(HttpContext context); /// /// Get a custom property from this scoped context /// T Get(); /// /// Add a custom property to this scoped context /// void Set(T value); /// /// Remove a custom property from this scoped context /// void Remove(); /// /// Scope the current context to a specific application database /// ZeroContextScope CreateScope(Application app); /// /// Scope the current context to a specific database /// ZeroContextScope CreateScope(string database); }