From 59bfc3a5370412c4d58b56921165015e4cdc2fbe Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Mon, 16 Nov 2020 01:07:07 +0100 Subject: [PATCH] output works again --- zero.Core/Api/BackofficeApi.cs | 9 +- zero.Core/Api/BackofficeStore.cs | 11 +- zero.Core/Api/SetupApi.cs | 2 +- ...ationContext.cs => ApplicationResolver.cs} | 171 +++++------------- zero.Core/Constants.cs | 2 +- zero.Core/Database/ZeroStore.cs | 104 +++++++++-- zero.Core/Entities/User/BackofficeUser.cs | 6 +- zero.Core/Extensions/HttpContextExtensions.cs | 16 ++ zero.Core/Extensions/ZeroStoreExtensions.cs | 44 ----- zero.Core/Identity/RavenRoleStore(TRole).cs | 10 +- zero.Core/Identity/RavenUserStore(TUser).cs | 22 +-- .../Identity/RavenUserStore(TUser,TRole).cs | 2 +- zero.Core/Routing/Routes.cs | 16 +- .../ZeroBackofficeClaimsPrincipalFactory.cs | 12 +- .../Security/ZeroClaimsPrinicipalFactory.cs | 2 - zero.Core/ZeroContext.cs | 54 +++--- .../Controllers/AuthenticationController.cs | 7 +- zero.Web/Controllers/ZeroController.cs | 3 +- zero.Web/Routing/ZeroRoutesTransformer.cs | 12 +- zero.Web/ZeroBuilder.cs | 2 +- zero.Web/ZeroVue.cs | 8 +- 21 files changed, 244 insertions(+), 271 deletions(-) rename zero.Core/{Api/ApplicationContext.cs => ApplicationResolver.cs} (63%) create mode 100644 zero.Core/Extensions/HttpContextExtensions.cs delete mode 100644 zero.Core/Extensions/ZeroStoreExtensions.cs diff --git a/zero.Core/Api/BackofficeApi.cs b/zero.Core/Api/BackofficeApi.cs index 4a0d627f..be8d437b 100644 --- a/zero.Core/Api/BackofficeApi.cs +++ b/zero.Core/Api/BackofficeApi.cs @@ -25,7 +25,14 @@ namespace zero.Core.Api protected bool IsCoreDatabase { get; private set; } - public BackofficeApi(IBackofficeStore store, bool isCoreDatabase = false) + public BackofficeApi(IBackofficeStore store) + { + Store = store.Store; + Backoffice = store; + IsCoreDatabase = false; + } + + internal BackofficeApi(IBackofficeStore store, bool isCoreDatabase) { Store = store.Store; Backoffice = store; diff --git a/zero.Core/Api/BackofficeStore.cs b/zero.Core/Api/BackofficeStore.cs index 5b2aef79..4cf65eaf 100644 --- a/zero.Core/Api/BackofficeStore.cs +++ b/zero.Core/Api/BackofficeStore.cs @@ -1,5 +1,4 @@ -using Raven.Client.Documents; -using zero.Core.Database; +using zero.Core.Database; using zero.Core.Messages; using zero.Core.Options; @@ -9,7 +8,7 @@ namespace zero.Core.Api { public IZeroStore Store { get; private set; } - public IApplicationContext AppContext { get; private set; } + public IZeroContext Context { get; private set; } public IZeroOptions Options { get; private set; } @@ -18,10 +17,10 @@ namespace zero.Core.Api public IMessageAggregator Messages { get; private set; } - public BackofficeStore(IZeroStore store, IApplicationContext appContext, IZeroOptions options, IAuthenticationApi authenticationApi, IMessageAggregator messages) + public BackofficeStore(IZeroStore store, IZeroContext context, IZeroOptions options, IAuthenticationApi authenticationApi, IMessageAggregator messages) { Store = store; - AppContext = appContext; + Context = context; Options = options; Auth = authenticationApi; Messages = messages; @@ -33,7 +32,7 @@ namespace zero.Core.Api { IZeroStore Store { get; } - IApplicationContext AppContext { get; } + IZeroContext Context { get; } IZeroOptions Options { get; } diff --git a/zero.Core/Api/SetupApi.cs b/zero.Core/Api/SetupApi.cs index a45452d4..7a0775f0 100644 --- a/zero.Core/Api/SetupApi.cs +++ b/zero.Core/Api/SetupApi.cs @@ -113,7 +113,7 @@ namespace zero.Core.Api await session.StoreAsync(app); // set app-id for user and store it - //user.AppId = session.Advanced.GetDocumentId(app); // TODO appx fix + user.AppId = session.Advanced.GetDocumentId(app); await session.StoreAsync(user); // save default user roles diff --git a/zero.Core/Api/ApplicationContext.cs b/zero.Core/ApplicationResolver.cs similarity index 63% rename from zero.Core/Api/ApplicationContext.cs rename to zero.Core/ApplicationResolver.cs index 891ec4e1..0267c3e5 100644 --- a/zero.Core/Api/ApplicationContext.cs +++ b/zero.Core/ApplicationResolver.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Logging; using Raven.Client.Documents; @@ -15,29 +16,23 @@ using zero.Core.Handlers; using zero.Core.Identity; using zero.Core.Options; -namespace zero.Core.Api +namespace zero.Core { - public class ApplicationContext : IApplicationContext + public class ApplicationResolver : IApplicationResolver { - /// - public IApplication App { get; protected set; } - - /// - public string AppId { get; protected set; } - protected IZeroStore Store { get; private set; } protected IZeroOptions Options { get; private set; } - protected ILogger Logger { get; private set; } + protected ILogger Logger { get; private set; } protected IHandlerHolder Handler { get; private set; } - static IList Apps { get; set; } + private IList Apps { get; set; } - public ApplicationContext(IZeroStore store, IZeroOptions options, ILogger logger, IHandlerHolder handler = null) + public ApplicationResolver(IZeroStore store, IZeroOptions options, ILogger logger, IHandlerHolder handler = null) { Store = store; Options = options; @@ -51,12 +46,13 @@ namespace zero.Core.Api { if (context?.Request == null) { + Logger.LogWarning("Could not resolve application as HTTP request is null"); return null; } IApplication app; - if (IsBackofficeRequest(context)) + if (context.IsBackofficeRequest(Options.BackofficePath)) { app = await ResolveFromUser(user); } @@ -72,45 +68,10 @@ namespace zero.Core.Api app = apps.FirstOrDefault(); } - App = app; - AppId = app?.Id; - return app; } - /// - public async Task TrySwitchForUser(IBackofficeUser user, string appId) - { - if (user == null || appId.IsNullOrEmpty()) - { - return false; - } - - string[] allowedAppIds = GetAllowedAppIdsForUser(user); - - bool isMainId = appId.Equals(user.AppId, StringComparison.InvariantCultureIgnoreCase); - bool isAllowedId = allowedAppIds.Contains(appId, StringComparer.InvariantCultureIgnoreCase); - - if (user.IsSuper || isMainId || isAllowedId) - { - user.CurrentAppId = appId; - - //byte[] bytes = new byte[20]; - //RandomNumberGenerator.Fill(bytes); - //user.SecurityStamp = Base32.ToBase32(bytes); // TODO update security stamp but Base32 is .net core internal - - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); - await session.StoreAsync(user); - await session.SaveChangesAsync(); - - return true; - } - - return false; - } - - /// public async Task ResolveFromUser(ClaimsPrincipal user) { @@ -151,7 +112,7 @@ namespace zero.Core.Api throw new Exception($"User entity ${user.Id} needs a valid AppId"); } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await session.LoadAsync(appId); } @@ -177,71 +138,30 @@ namespace zero.Core.Api } - /// - public async Task ForId(string appId) - { - using IAsyncDocumentSession session = Store.OpenAsyncSession(); - IApplication app = await session.LoadAsync(appId); - - return new ApplicationContext(Store, Options, Logger, Handler) - { - App = app, - AppId = appId - }; - } - - - /// - public bool IsBackofficeRequest(HttpContext context) - { - string path = Options.BackofficePath.EnsureStartsWith('/').TrimEnd('/'); - return context.Request.Path.ToString().StartsWith(path); - } - - /// /// Get matching application from an URI /// IApplication ResolveFromUriInternal(Uri uri, IList apps) { - string[] protocols = new string[3] { "https://", "http://", "//" }; - - IApplication currentApp = null; - foreach (IApplication app in apps) { if (app.Domains?.Length < 1) { + Logger.LogWarning("No domains specified for app {app}", app.Id); continue; } foreach (Uri domain in app.Domains) { - //string normalizedDomain = domain; - - //if (!protocols.Any(protocol => domain.StartsWith(protocol, StringComparison.OrdinalIgnoreCase))) - //{ - // normalizedDomain = "http://" + domain; - //} - - //UriBuilder uriBuilder = new UriBuilder(normalizedDomain); - - //if (!uriBuilder.Uri.IsAbsoluteUri) - //{ - // continue; - //} - int compareResult = Uri.Compare(uri, domain, UriComponents.HostAndPort, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase); - if (compareResult == 0) { - currentApp = app; - break; + return app; } } } - return currentApp; + return null; } @@ -255,7 +175,7 @@ namespace zero.Core.Api return Apps; } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); Apps = await session.Query().ToListAsync(); return Apps; } @@ -268,7 +188,7 @@ namespace zero.Core.Api { string userId = user.FindFirstValue(Constants.Auth.Claims.UserId); - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await session.LoadAsync(userId); } @@ -284,34 +204,49 @@ namespace zero.Core.Api return permissions.Where(x => x.IsTrue).Select(x => x.NormalizedKey).ToArray(); } + + + /// + //public async Task TrySwitchForUser(IBackofficeUser user, string appId) + //{ + // if (user == null || appId.IsNullOrEmpty()) + // { + // return false; + // } + + // string[] allowedAppIds = GetAllowedAppIdsForUser(user); + + // bool isMainId = appId.Equals(user.AppId, StringComparison.InvariantCultureIgnoreCase); + // bool isAllowedId = allowedAppIds.Contains(appId, StringComparer.InvariantCultureIgnoreCase); + + // if (user.IsSuper || isMainId || isAllowedId) + // { + // user.CurrentAppId = appId; + + // //byte[] bytes = new byte[20]; + // //RandomNumberGenerator.Fill(bytes); + // //user.SecurityStamp = Base32.ToBase32(bytes); // TODO update security stamp but Base32 is .net core internal + + // using IAsyncDocumentSession session = Store.OpenCoreSession(); + // await session.StoreAsync(user); + // await session.SaveChangesAsync(); + + // return true; + // } + + // return false; + //} } - - public interface IApplicationContext + public interface IApplicationResolver { - /// - /// Currently loaded application - /// - IApplication App { get; } - - /// - /// Current loaded application Id - /// - string AppId { get; } - /// /// Resolves the current application from either the backoffice user (in case it is backoffice request) /// or the domain (in case it is frontend request). - /// The resolved data is stored in the App + AppId properties. /// Task Resolve(HttpContext context, ClaimsPrincipal user); - /// - /// Try to switch the current application for a user - /// - Task TrySwitchForUser(IBackofficeUser user, string appId); - /// /// Resolves the current application from the request path /// @@ -338,15 +273,5 @@ namespace zero.Core.Api /// This method won't return apps the user has no access to. /// Task ResolveFromUser(IBackofficeUser user); - - /// - /// Creates a new application context for the specified application. - /// - Task ForId(string appId); - - /// - /// Whether the current request is a backoffice request - /// - bool IsBackofficeRequest(HttpContext context); } } diff --git a/zero.Core/Constants.cs b/zero.Core/Constants.cs index e4f422ec..ea528e5d 100644 --- a/zero.Core/Constants.cs +++ b/zero.Core/Constants.cs @@ -28,7 +28,7 @@ public const string Email = "zero.claim.email"; public const string Permission = "zero.claim.permission"; public const string DefaultAppId = "zero.claim.defaultAppId"; - public const string AppId = "zero.claim.appId"; // TODO appx fix + public const string AppId = "zero.claim.appId"; public const string TicketExpires = "zero.claim.ticketExpires"; } } diff --git a/zero.Core/Database/ZeroStore.cs b/zero.Core/Database/ZeroStore.cs index 2936967b..aa4e932a 100644 --- a/zero.Core/Database/ZeroStore.cs +++ b/zero.Core/Database/ZeroStore.cs @@ -1,29 +1,28 @@ -using Raven.Client.Documents; +using Raven.Client; +using Raven.Client.Documents; using Raven.Client.Documents.BulkInsert; using Raven.Client.Documents.Operations; +using Raven.Client.Documents.Queries; using Raven.Client.Documents.Session; +using System; using System.Threading; using System.Threading.Tasks; -using zero.Core.Extensions; using zero.Core.Options; namespace zero.Core.Database { public class ZeroStore : DocumentStore, IZeroStore { - protected IZeroOptions Options { get; set; } - public ZeroStore(IZeroOptions options) : base() { Options = options; Database = null; } + protected IZeroOptions Options { get; set; } - internal async Task SetContext() - { - - } + /// + public string ResolvedDatabase { get; set; } /// @@ -33,20 +32,83 @@ namespace zero.Core.Database /// public OperationExecutor GetOperationExecutor(string database = null) { - if (database.IsNullOrEmpty()) + return Operations.ForDatabase(database ?? ResolvedDatabase); + } + + /// + public IAsyncDocumentSession OpenCoreSession() + { + return OpenAsyncSession(Options.Raven.Database); + } + + /// + public override IAsyncDocumentSession OpenAsyncSession(string database) + { + return base.OpenAsyncSession(database); + } + + /// + public override IAsyncDocumentSession OpenAsyncSession(SessionOptions options) + { + options.Database = options.Database ?? ResolvedDatabase; + return base.OpenAsyncSession(options); + } + + /// + public override IAsyncDocumentSession OpenAsyncSession() + { + return base.OpenAsyncSession(ResolvedDatabase); + } + + /// + public override IDocumentSession OpenSession(SessionOptions options) + { + options.Database = options.Database ?? ResolvedDatabase; + return base.OpenSession(options); + } + + /// + public override IDocumentSession OpenSession(string database) + { + return base.OpenSession(database); + } + + /// + public override IDocumentSession OpenSession() + { + return base.OpenSession(ResolvedDatabase); + } + + /// + public override BulkInsertOperation BulkInsert(string database = null, CancellationToken token = default) + { + return base.BulkInsert(database ?? ResolvedDatabase, token); + } + + /// + public async Task PurgeAsync(string database = null, string querySuffix = null, Parameters parameters = null) + { + var collectionName = Conventions.FindCollectionName(typeof(T)); + var operationQuery = new DeleteByQueryOperation(new IndexQuery() { - return Operations; - } - else - { - return Operations.ForDatabase(database); - } + Query = $"from {collectionName} c {querySuffix ?? String.Empty}", + QueryParameters = parameters + }, new QueryOperationOptions { AllowStale = true }); + + Operation operation = await GetOperationExecutor(database).SendAsync(operationQuery); + + await operation.WaitForCompletionAsync(); } } public interface IZeroStore : IDocumentStore { + /// + /// The database which has been resolved from the current application + /// + string ResolvedDatabase { get; set; } + /// /// Get underlying raven document store /// @@ -55,6 +117,16 @@ namespace zero.Core.Database /// /// Get operation executor /// - public OperationExecutor GetOperationExecutor(string database = null); + OperationExecutor GetOperationExecutor(string database = null); + + /// + /// Purges a collection + /// + Task PurgeAsync(string database = null, string querySuffix = null, Parameters parameters = null); + + /// + /// Opens a session for the core database + /// + IAsyncDocumentSession OpenCoreSession(); } } diff --git a/zero.Core/Entities/User/BackofficeUser.cs b/zero.Core/Entities/User/BackofficeUser.cs index 9b63e8d4..29d6d7d1 100644 --- a/zero.Core/Entities/User/BackofficeUser.cs +++ b/zero.Core/Entities/User/BackofficeUser.cs @@ -81,14 +81,14 @@ namespace zero.Core.Entities public interface IBackofficeUser : IZeroEntity, IZeroDbConventions, IIdentityUserWithRoles { /// - /// Application ID the user was created in + /// Application the user registered in /// - public string AppId { get; set; } + string AppId { get; set; } /// /// Currently selected app id for the backoffice /// - public string CurrentAppId { get; set; } + string CurrentAppId { get; set; } /// /// sudo. diff --git a/zero.Core/Extensions/HttpContextExtensions.cs b/zero.Core/Extensions/HttpContextExtensions.cs new file mode 100644 index 00000000..c7cd9662 --- /dev/null +++ b/zero.Core/Extensions/HttpContextExtensions.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.Http; + +namespace zero.Core.Extensions +{ + internal static class HttpContextExtensions + { + /// + /// Whether the current request is a backoffice request + /// + public static bool IsBackofficeRequest(this HttpContext context, string backofficePath) + { + string path = backofficePath.EnsureStartsWith('/').TrimEnd('/'); + return context.Request.Path.ToString().StartsWith(path); + } + } +} diff --git a/zero.Core/Extensions/ZeroStoreExtensions.cs b/zero.Core/Extensions/ZeroStoreExtensions.cs deleted file mode 100644 index be7ef806..00000000 --- a/zero.Core/Extensions/ZeroStoreExtensions.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Raven.Client; -using Raven.Client.Documents.Operations; -using Raven.Client.Documents.Queries; -using Raven.Client.Documents.Session; -using System; -using System.Threading.Tasks; -using zero.Core.Database; -using zero.Core.Options; - -namespace zero.Core.Extensions -{ - internal static class ZeroStoreExtensions - { - /// - /// Opens a session for the core database - /// - public static IAsyncDocumentSession OpenCoreSession(this IZeroStore store, IZeroOptions options) - { - return store.OpenAsyncSession(options.Raven.Database); - } - - - /// - /// Purges a collection - /// - public static async Task PurgeAsync(this IZeroStore store, string database = null, string querySuffix = null, Parameters parameters = null) - { - var collectionName = store.Conventions.FindCollectionName(typeof(T)); - - var operationQuery = new DeleteByQueryOperation(new IndexQuery() - { - Query = $"from {collectionName} c {querySuffix ?? String.Empty}", - QueryParameters = parameters - }, new QueryOperationOptions - { - AllowStale = true - }); - - Operation operation = await store.GetOperationExecutor(database).SendAsync(operationQuery); - - await operation.WaitForCompletionAsync(); - } - } -} diff --git a/zero.Core/Identity/RavenRoleStore(TRole).cs b/zero.Core/Identity/RavenRoleStore(TRole).cs index d119ecd9..a4e4d87e 100644 --- a/zero.Core/Identity/RavenRoleStore(TRole).cs +++ b/zero.Core/Identity/RavenRoleStore(TRole).cs @@ -56,7 +56,7 @@ namespace zero.Core.Identity Description = $"The affected role is is not part of this role store and can't be created." }); } - using (IAsyncDocumentSession session = Store.OpenCoreSession(Options)) + using (IAsyncDocumentSession session = Store.OpenCoreSession()) { await session.StoreAsync(role); await session.SaveChangesAsync(cancellationToken); @@ -78,7 +78,7 @@ namespace zero.Core.Identity } try { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); await session.StoreAsync(role, cancellationToken); await session.SaveChangesAsync(cancellationToken); } @@ -103,7 +103,7 @@ namespace zero.Core.Identity } try { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); session.Delete(role); await session.SaveChangesAsync(cancellationToken); } @@ -145,7 +145,7 @@ namespace zero.Core.Identity /// public async Task FindByIdAsync(string roleId, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).FirstOrDefaultAsync(x => x.Id == roleId, cancellationToken); } @@ -153,7 +153,7 @@ namespace zero.Core.Identity /// public async Task FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).FirstOrDefaultAsync(x => x.Name == normalizedRoleName, cancellationToken); } diff --git a/zero.Core/Identity/RavenUserStore(TUser).cs b/zero.Core/Identity/RavenUserStore(TUser).cs index b3446cfd..9dbf9ca1 100644 --- a/zero.Core/Identity/RavenUserStore(TUser).cs +++ b/zero.Core/Identity/RavenUserStore(TUser).cs @@ -66,7 +66,7 @@ namespace zero.Core.Identity }); } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); // try to reserve the key for the new user if (!await Store.Raven.ReserveAsync(GetReservationKey(user), user.Id)) @@ -116,7 +116,7 @@ namespace zero.Core.Identity // Delete the user and save it. We must save it because deleting is a cluster-wide operation. // Only if the deletion succeeds will we remove the cluster-wide compare/exchange key. - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); TUser source = await session.LoadAsync(user.Id, cancellationToken); @@ -139,7 +139,7 @@ namespace zero.Core.Identity }); } - using (IAsyncDocumentSession session = Store.OpenCoreSession(Options)) + using (IAsyncDocumentSession session = Store.OpenCoreSession()) { TUser source = await session.LoadAsync(user.Id, cancellationToken); @@ -169,7 +169,7 @@ namespace zero.Core.Identity } } - using (IAsyncDocumentSession session = Store.OpenCoreSession(Options)) + using (IAsyncDocumentSession session = Store.OpenCoreSession()) { await session.StoreAsync(user, cancellationToken); await session.SaveChangesAsync(cancellationToken); @@ -182,7 +182,7 @@ namespace zero.Core.Identity /// public async Task FindByIdAsync(string userId, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).FirstOrDefaultAsync(x => x.Id == userId, cancellationToken); } @@ -190,7 +190,7 @@ namespace zero.Core.Identity /// public async Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).FirstOrDefaultAsync(x => x.Username == normalizedUserName, cancellationToken); } @@ -258,7 +258,7 @@ namespace zero.Core.Identity /// public async Task FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).FirstOrDefaultAsync(x => x.Email == normalizedEmail, cancellationToken); } @@ -389,7 +389,7 @@ namespace zero.Core.Identity return; } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); user.Claims.AddRange(claims.Select(claim => new UserClaim(claim))); await session.StoreAsync(user, cancellationToken); @@ -407,7 +407,7 @@ namespace zero.Core.Identity /// public async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); UserClaim userClaim = new UserClaim(claim); return await ScopeQuery(session.Query()).Where(x => x.Claims.Any(c => c.Type == userClaim.Type && c.Value == userClaim.Value)).ToListAsync(); } @@ -422,7 +422,7 @@ namespace zero.Core.Identity return; } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); IEnumerable userClaims = claims.Select(c => new UserClaim(c)).ToList(); @@ -443,7 +443,7 @@ namespace zero.Core.Identity return; } - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); UserClaim userClaim = new UserClaim(claim); UserClaim newUserClaim = new UserClaim(newClaim); diff --git a/zero.Core/Identity/RavenUserStore(TUser,TRole).cs b/zero.Core/Identity/RavenUserStore(TUser,TRole).cs index 9069a919..5893eacd 100644 --- a/zero.Core/Identity/RavenUserStore(TUser,TRole).cs +++ b/zero.Core/Identity/RavenUserStore(TUser,TRole).cs @@ -42,7 +42,7 @@ namespace zero.Core.Identity /// public async Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken) { - using IAsyncDocumentSession session = Store.OpenCoreSession(Options); + using IAsyncDocumentSession session = Store.OpenCoreSession(); return await ScopeQuery(session.Query()).Where(x => roleName.In(x.RoleIds)).ToListAsync(); // TODO scope } diff --git a/zero.Core/Routing/Routes.cs b/zero.Core/Routing/Routes.cs index dceb0c52..38abdc69 100644 --- a/zero.Core/Routing/Routes.cs +++ b/zero.Core/Routing/Routes.cs @@ -12,6 +12,7 @@ using zero.Core.Api; using zero.Core.Database; using zero.Core.Entities; using zero.Core.Extensions; +using zero.Core.Options; namespace zero.Core.Routing { @@ -20,15 +21,17 @@ namespace zero.Core.Routing protected IZeroStore Store { get; set; } protected ILogger Logger { get; set; } protected IEnumerable Providers { get; set; } - protected IApplicationContext Context { get; set; } + protected IApplicationResolver AppResolver { get; set; } + protected IZeroOptions Options { get; set; } - public Routes(IZeroStore store, ILogger logger, IEnumerable providers, IApplicationContext context) + public Routes(IZeroStore store, ILogger logger, IEnumerable providers, IApplicationResolver appResolver, IZeroOptions options) { Store = store; Logger = logger; Providers = providers; - Context = context; + AppResolver = appResolver; + Options = options; } @@ -128,7 +131,12 @@ namespace zero.Core.Routing /// public async Task ResolveUrl(HttpContext context) { - IApplication app = await Context.ResolveFromRequest(context); + if (context.IsBackofficeRequest(Options.BackofficePath)) + { + return null; + } + + IApplication app = await AppResolver.ResolveFromRequest(context); string path = context.Request.Path; if (app == null) diff --git a/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs b/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs index 3874e8c0..ac4f5b23 100644 --- a/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs +++ b/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs @@ -55,16 +55,16 @@ namespace zero.Core.Security .Select(x => x.NormalizedKey) .ToArray(); - string currentAppId = user.CurrentAppId; // ?? user.AppId; // TODO appx fix + string currentAppId = user.CurrentAppId ?? user.AppId; - //if (!user.IsSuper && !appIds.Contains(currentAppId)) - //{ - // currentAppId = user.AppId; - //} + if (!user.IsSuper && !appIds.Contains(currentAppId)) + { + currentAppId = user.AppId; + } claims.RemoveAll(x => x.Type == Constants.Auth.Claims.AppId); claims.Add(new Claim(Constants.Auth.Claims.AppId, currentAppId)); - //claims.Add(new Claim(Constants.Auth.Claims.DefaultAppId, user.AppId)); + claims.Add(new Claim(Constants.Auth.Claims.DefaultAppId, user.AppId)); return claims; } diff --git a/zero.Core/Security/ZeroClaimsPrinicipalFactory.cs b/zero.Core/Security/ZeroClaimsPrinicipalFactory.cs index 5d725d1e..0f265e01 100644 --- a/zero.Core/Security/ZeroClaimsPrinicipalFactory.cs +++ b/zero.Core/Security/ZeroClaimsPrinicipalFactory.cs @@ -137,8 +137,6 @@ namespace zero.Core.Security claims.Add(new Claim(Constants.Auth.Claims.Email, await UserManager.GetEmailAsync(user))); } - //claims.Add(new Claim(Constants.Auth.Claims.AppId, user.AppId)); // TODO appx fix - return claims; } } diff --git a/zero.Core/ZeroContext.cs b/zero.Core/ZeroContext.cs index 3e6fafd9..fe8a7751 100644 --- a/zero.Core/ZeroContext.cs +++ b/zero.Core/ZeroContext.cs @@ -3,9 +3,9 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using System.Security.Claims; using System.Threading.Tasks; -using zero.Core.Api; +using zero.Core.Database; using zero.Core.Entities; -using zero.Core.Identity; +using zero.Core.Extensions; using zero.Core.Options; using zero.Core.Routing; @@ -14,7 +14,7 @@ namespace zero.Core public class ZeroContext : IZeroContext { /// - public IApplication App { get; protected set; } + public IApplication Application { get; protected set; } /// public string AppId { get; protected set; } @@ -22,9 +22,6 @@ namespace zero.Core /// public ClaimsPrincipal BackofficeUser { get; protected set; } - /// - public ClaimsIdentity BackofficeIdentity { get; protected set; } - /// public bool IsBackofficeRequest { get; protected set; } @@ -38,50 +35,58 @@ namespace zero.Core public IResolvedRoute ResolvedRoute { get; private set; } - protected IApplicationContext AppContext { get; private set; } + protected IApplicationResolver AppResolver { get; private set; } protected ILogger Logger { get; private set; } + protected IZeroStore Store { get; private set; } + private bool _resolved = false; - public ZeroContext(IZeroOptions options, IApplicationContext appContext, ILogger logger) + public ZeroContext(IZeroOptions options, IApplicationResolver appResolver, ILogger logger, IZeroStore store) { Options = options; - AppContext = appContext; + AppResolver = appResolver; Logger = logger; + Store = store; } /// public async virtual Task Resolve(HttpContext context) { - if (_resolved || context?.Request is null) + if (_resolved) { return; } + if (context?.Request is null) + { + Store.ResolvedDatabase = null; + return; + } + _resolved = true; + // check if the current request is a backoffice request + IsBackofficeRequest = context.IsBackofficeRequest(Options.BackofficePath); + + // get the currently logged-in backoffice user + BackofficeUser = new ClaimsPrincipal(); AuthenticateResult authResult = await context.AuthenticateAsync(Constants.Auth.BackofficeScheme); if (authResult?.Principal is not null) { BackofficeUser = authResult.Principal; - if (BackofficeUserIdentity.TryGet(authResult.Principal, out BackofficeUserIdentity identity)) - { - BackofficeIdentity = identity; - } - } - else - { - BackofficeUser = new ClaimsPrincipal(); } - App = await AppContext.Resolve(context, BackofficeUser); - AppId = App.Id; + // resolve current application + Application = await AppResolver.Resolve(context, BackofficeUser); + AppId = Application.Id; - IsBackofficeRequest = AppContext.IsBackofficeRequest(context); + // set default database for document store + Store.ResolvedDatabase = Application.Database; if (IsBackofficeRequest is false && context.Request.RouteValues.TryGetValue("zero.route", out object route)) { @@ -97,7 +102,7 @@ namespace zero.Core /// /// Currently loaded application /// - IApplication App { get; } + IApplication Application { get; } /// /// Current loaded application Id @@ -109,11 +114,6 @@ namespace zero.Core /// ClaimsPrincipal BackofficeUser { get; } - /// - /// Resolved backoffice user identity - /// - ClaimsIdentity BackofficeIdentity { get; } - /// /// Whether the current request is a backoffice request or not /// diff --git a/zero.Web/Controllers/AuthenticationController.cs b/zero.Web/Controllers/AuthenticationController.cs index 7a4255dd..79e5cd12 100644 --- a/zero.Web/Controllers/AuthenticationController.cs +++ b/zero.Web/Controllers/AuthenticationController.cs @@ -11,13 +11,11 @@ namespace zero.Web.Controllers public class AuthenticationController : BackofficeController { IAuthenticationApi Api; - IApplicationContext AppContext; - public AuthenticationController(IAuthenticationApi api, IApplicationContext appContext) + public AuthenticationController(IAuthenticationApi api) { Api = api; - AppContext = appContext; } @@ -43,7 +41,8 @@ namespace zero.Web.Controllers public async Task SwitchApp(string appId) { BackofficeUser user = await Api.GetUser(); - return EntityResult.Maybe(await AppContext.TrySwitchForUser(user, appId)); + return EntityResult.Fail(); + //return EntityResult.Maybe(await AppContext.TrySwitchForUser(user, appId)); // TODO appx } } } diff --git a/zero.Web/Controllers/ZeroController.cs b/zero.Web/Controllers/ZeroController.cs index cbe43c0a..1800e1a6 100644 --- a/zero.Web/Controllers/ZeroController.cs +++ b/zero.Web/Controllers/ZeroController.cs @@ -5,6 +5,7 @@ using zero.Core.Api; using zero.Core.Entities; using zero.Core.Routing; using Microsoft.Extensions.DependencyInjection; +using zero.Core; namespace zero.Web.Controllers { @@ -23,7 +24,7 @@ namespace zero.Web.Controllers public override void OnActionExecuting(ActionExecutingContext filterContext) { - Application = filterContext.HttpContext.RequestServices.GetService()?.App; + Application = filterContext.HttpContext.RequestServices.GetService()?.Application; if (filterContext.RouteData.Values.TryGetValue("zero.route", out object route)) { diff --git a/zero.Web/Routing/ZeroRoutesTransformer.cs b/zero.Web/Routing/ZeroRoutesTransformer.cs index b857997b..65b2ed17 100644 --- a/zero.Web/Routing/ZeroRoutesTransformer.cs +++ b/zero.Web/Routing/ZeroRoutesTransformer.cs @@ -2,30 +2,22 @@ using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Routing; using System.Threading.Tasks; -using zero.Core.Api; using zero.Core.Routing; namespace zero.Web.Routing { public class ZeroRoutesTransformer : DynamicRouteValueTransformer { - Core.Routing.IRoutes Routes; - IApplicationContext Context; + IRoutes Routes; - public ZeroRoutesTransformer(Core.Routing.IRoutes routes, IApplicationContext context) + public ZeroRoutesTransformer(IRoutes routes) { Routes = routes; - Context = context; } public override async ValueTask TransformAsync(HttpContext httpContext, RouteValueDictionary values) { - if (Context.IsBackofficeRequest(httpContext)) - { - return null; - } - IResolvedRoute route = await Routes.ResolveUrl(httpContext); if (route == null) diff --git a/zero.Web/ZeroBuilder.cs b/zero.Web/ZeroBuilder.cs index 4b0b2a04..aee62e04 100644 --- a/zero.Web/ZeroBuilder.cs +++ b/zero.Web/ZeroBuilder.cs @@ -104,7 +104,7 @@ namespace zero.Web // add default services Services.AddHttpContextAccessor(); - Services.AddScoped(); + Services.AddScoped(); Services.AddScoped(); Services.AddScoped(); diff --git a/zero.Web/ZeroVue.cs b/zero.Web/ZeroVue.cs index 3a17fe96..9bccb9c7 100644 --- a/zero.Web/ZeroVue.cs +++ b/zero.Web/ZeroVue.cs @@ -29,17 +29,17 @@ namespace zero.Web protected IEnumerable Plugins { get; private set; } - protected IApplicationContext AppContext { get; private set; } + protected IZeroContext Context { get; private set; } - public ZeroVue(IZeroOptions options, IWebHostEnvironment env, IApplicationsApi applicationsApi, IAuthenticationApi authenticationApi, IEnumerable plugins, IApplicationContext appContext) + public ZeroVue(IZeroOptions options, IWebHostEnvironment env, IApplicationsApi applicationsApi, IAuthenticationApi authenticationApi, IEnumerable plugins, IZeroContext context) { Environment = env; Options = options; ApplicationsApi = applicationsApi; AuthenticationApi = authenticationApi; Plugins = plugins; - AppContext = appContext; + Context = context; //zero.path = "@Model.BackofficePath.EnsureEndsWith('/')"; //zero.translations = @Html.Raw(text); } @@ -61,7 +61,7 @@ namespace zero.Web config.Applications = await CreateApplications(); config.Alias = CreateAliases(); config.SettingsAreas = CreateSettingsAreas(); - config.AppId = AppContext.AppId; + config.AppId = Context.AppId; //config.SharedAppId = Constants.Database.SharedAppId; // TODO appx BackofficeUser user = await AuthenticationApi.GetUser();