From 691a660ec3a41ebb247c43bc7e7e8e905803a35e Mon Sep 17 00:00:00 2001 From: Tobias Klika Date: Tue, 23 Nov 2021 22:56:22 +0100 Subject: [PATCH] modules => serviceCollectionExtensions --- .../Configuration/BackofficeOptions.cs | 5 + .../Controllers/ZeroIndexController.cs | 39 +++ .../Controllers/ZeroVueController.cs | 33 -- .../_ZeroBackofficeCollectionController.cs | 2 +- .../Modules/Countries/CountriesController.cs | 4 +- .../Modules/Languages/LanguagesController.cs | 4 +- .../Modules/Mails/MailTemplatesController.cs | 4 +- zero.Backoffice/Plugin.cs | 23 +- zero.Backoffice/Registrations.cs | 3 +- .../ServiceCollectionExtensions.cs | 36 +++ zero.Backoffice/Usings.cs | 5 +- zero.Backoffice/ZeroVue.cs | 8 +- .../Controllers/AuthenticationController.cs | 6 +- .../_legacy/Controllers/SettingsController.cs | 4 +- .../Controllers/UserRolesController.cs | 14 +- .../_legacy/Controllers/UsersController.cs | 38 +-- .../_legacy/Defaults/ZeroBackofficePlugin.cs | 8 +- zero.Backoffice/zero.Backoffice.csproj | 34 --- zero.Core/Applications/ApplicationResolver.cs | 10 +- zero.Core/Applications/Module.cs | 12 - .../ServiceCollectionExtensions.cs | 12 + zero.Core/Architecture/Module.cs | 14 - zero.Core/Architecture/Modules/ZeroModule.cs | 8 - .../Modules/ZeroModuleConfiguration.cs | 25 -- .../Modules/ZeroModuleInitializer.cs | 20 -- .../ServiceCollectionExtensions.cs | 14 + zero.Core/Collections/CountriesCollection.cs | 18 -- zero.Core/Communication/Module.cs | 14 - .../ServiceCollectionExtensions.cs | 15 + .../Integrations/IntegrationService.cs | 2 +- zero.Core/Configuration/Module.cs | 14 - .../ServiceCollectionExtensions.cs | 15 + zero.Core/Context/Module.cs | 13 - .../Context/ServiceCollectionExtensions.cs | 13 + .../ServiceCollectionExtensions.cs | 13 + zero.Core/Identity/Api/AuthenticationApi.cs | 228 -------------- zero.Core/Identity/Api/AuthorizationApi.cs | 143 --------- zero.Core/Identity/Api/PermissionsApi.cs | 58 ---- .../AuthenticationBuilderExtensions.cs | 4 +- .../Identity/BackofficeUserExtensions.cs | 2 +- zero.Core/Identity/Collections/UserApi.cs | 283 ------------------ .../Identity/Collections/UserRolesApi.cs | 125 -------- zero.Core/Identity/IdentityModule.cs | 32 -- zero.Core/Identity/Models/UserClaim.cs | 22 +- .../Identity/Models/UserClaimComparer.cs | 14 + zero.Core/Identity/Models/ZeroIdentityRole.cs | 4 +- zero.Core/Identity/Models/ZeroIdentityUser.cs | 4 +- .../Models/{BackofficeUser.cs => ZeroUser.cs} | 5 +- ...{BackofficeUserRole.cs => ZeroUserRole.cs} | 2 +- zero.Core/Identity/RavenRoleStore(TRole).cs | 3 - zero.Core/Identity/RavenUserStore(TUser).cs | 4 - .../Identity/RavenUserStore(TUser,TRole).cs | 1 - .../Identity/Security/SchemedSignInManager.cs | 3 - .../Security/ZeroAuthorizeAttribute.cs | 2 - .../ZeroBackofficeClaimsPrincipalFactory.cs | 7 +- .../Security/ZeroClaimsPrinicipalFactory.cs | 2 - .../Identity/ServiceCollectionExtensions.cs | 31 ++ .../Services/AuthenticationService.cs | 218 ++++++++++++++ .../Identity/Services/AuthorizationService.cs | 135 +++++++++ .../Identity/Services/UserRolesService.cs | 114 +++++++ zero.Core/Identity/Services/UserService.cs | 275 +++++++++++++++++ zero.Core/Identity/UserIdentity.cs | 3 +- .../Validation/BackofficeUserRoleValidator.cs | 3 +- .../Validation/BackofficeUserValidator.cs | 2 +- zero.Core/Localization/Module.cs | 13 - .../ServiceCollectionExtensions.cs | 13 + zero.Core/Mails/MailProvider.cs | 4 +- zero.Core/Mails/Module.cs | 13 - .../Mails/ServiceCollectionExtensions.cs | 13 + zero.Core/Pages/PagesModule.cs | 13 - .../Pages/ServiceCollectionExtensions.cs | 19 ++ ...dule.cs => ServiceCollectionExtensions.cs} | 25 +- zero.Core/Registrations.cs | 26 -- zero.Core/Rendering/Module.cs | 12 - .../Rendering/ServiceCollectionExtensions.cs | 12 + zero.Core/Routing/Module.cs | 37 --- .../Routing/ServiceCollectionExtensions.cs | 34 +++ .../ZeroEnpointRouteBuilderExtensions.cs | 2 +- .../CachableEntityStore.cs} | 12 +- zero.Core/Stores/CountriesStore.cs | 18 ++ .../EntityStore.cs} | 10 +- .../LanguagesStore.cs} | 8 +- .../MailTemplatesStore.cs} | 8 +- .../Stores/ServiceCollectionExtensions.cs | 19 ++ .../StoreCache.cs} | 12 +- .../StoreContext.cs} | 12 +- .../StoreOperations.Delete.cs} | 4 +- .../StoreOperations.Read.cs} | 4 +- .../StoreOperations.Write.cs} | 4 +- .../StoreOperations.cs} | 8 +- zero.Core/Usings.cs | 2 +- zero.Core/ZeroApplicationBuilder.cs | 3 + zero.Core/ZeroBuilder.cs | 59 ++-- zero.Core/_legacy/Api/ApplicationsApi.cs | 2 +- zero.Core/_legacy/Api/BackofficeApi.cs | 6 +- zero.Core/_legacy/Api/BackofficeStore.cs | 6 +- zero.Core/_legacy/Api/ModulesApi.cs | 2 +- zero.Core/_legacy/Api/PreviewApi.cs | 2 +- zero.Core/_legacy/Api/RecycleBinApi.cs | 2 +- zero.Core/_legacy/Api/RevisionsApi.cs | 4 +- zero.Core/_legacy/Api/SetupApi.cs | 20 +- zero.Core/_legacy/Api/SpacesApi.cs | 4 +- .../Integrations/IntegrationsCollection.cs | 4 +- .../Collections/Media/MediaCollection.cs | 6 +- .../Media/MediaFolderCollection.cs | 6 +- .../Collections/Pages/PagesCollection.cs | 2 +- .../Translations/TranslationsCollection.cs | 6 +- zero.Core/zero.Core.csproj | 4 - 108 files changed, 1284 insertions(+), 1442 deletions(-) create mode 100644 zero.Backoffice/Controllers/ZeroIndexController.cs delete mode 100644 zero.Backoffice/Controllers/ZeroVueController.cs create mode 100644 zero.Backoffice/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Applications/Module.cs create mode 100644 zero.Core/Applications/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Architecture/Module.cs delete mode 100644 zero.Core/Architecture/Modules/ZeroModule.cs delete mode 100644 zero.Core/Architecture/Modules/ZeroModuleConfiguration.cs delete mode 100644 zero.Core/Architecture/Modules/ZeroModuleInitializer.cs create mode 100644 zero.Core/Architecture/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Collections/CountriesCollection.cs delete mode 100644 zero.Core/Communication/Module.cs create mode 100644 zero.Core/Communication/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Configuration/Module.cs create mode 100644 zero.Core/Configuration/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Context/Module.cs create mode 100644 zero.Core/Context/ServiceCollectionExtensions.cs create mode 100644 zero.Core/FileStorage/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Identity/Api/AuthenticationApi.cs delete mode 100644 zero.Core/Identity/Api/AuthorizationApi.cs delete mode 100644 zero.Core/Identity/Api/PermissionsApi.cs delete mode 100644 zero.Core/Identity/Collections/UserApi.cs delete mode 100644 zero.Core/Identity/Collections/UserRolesApi.cs delete mode 100644 zero.Core/Identity/IdentityModule.cs create mode 100644 zero.Core/Identity/Models/UserClaimComparer.cs rename zero.Core/Identity/Models/{BackofficeUser.cs => ZeroUser.cs} (74%) rename zero.Core/Identity/Models/{BackofficeUserRole.cs => ZeroUserRole.cs} (84%) create mode 100644 zero.Core/Identity/ServiceCollectionExtensions.cs create mode 100644 zero.Core/Identity/Services/AuthenticationService.cs create mode 100644 zero.Core/Identity/Services/AuthorizationService.cs create mode 100644 zero.Core/Identity/Services/UserRolesService.cs create mode 100644 zero.Core/Identity/Services/UserService.cs delete mode 100644 zero.Core/Localization/Module.cs create mode 100644 zero.Core/Localization/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Mails/Module.cs create mode 100644 zero.Core/Mails/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Pages/PagesModule.cs create mode 100644 zero.Core/Pages/ServiceCollectionExtensions.cs rename zero.Core/Persistence/{Module.cs => ServiceCollectionExtensions.cs} (57%) delete mode 100644 zero.Core/Registrations.cs delete mode 100644 zero.Core/Rendering/Module.cs create mode 100644 zero.Core/Rendering/ServiceCollectionExtensions.cs delete mode 100644 zero.Core/Routing/Module.cs create mode 100644 zero.Core/Routing/ServiceCollectionExtensions.cs rename zero.Core/{Collections/CachableEntityCollection.cs => Stores/CachableEntityStore.cs} (55%) create mode 100644 zero.Core/Stores/CountriesStore.cs rename zero.Core/{Collections/EntityCollection.cs => Stores/EntityStore.cs} (94%) rename zero.Core/{Collections/LanguagesCollection.cs => Stores/LanguagesStore.cs} (87%) rename zero.Core/{Collections/MailTemplatesCollection.cs => Stores/MailTemplatesStore.cs} (73%) create mode 100644 zero.Core/Stores/ServiceCollectionExtensions.cs rename zero.Core/{Collections/CollectionCache.cs => Stores/StoreCache.cs} (60%) rename zero.Core/{Collections/CollectionContext.cs => Stores/StoreContext.cs} (60%) rename zero.Core/{Collections/CollectionOperations.Delete.cs => Stores/StoreOperations.Delete.cs} (94%) rename zero.Core/{Collections/CollectionOperations.Read.cs => Stores/StoreOperations.Read.cs} (97%) rename zero.Core/{Collections/CollectionOperations.Write.cs => Stores/StoreOperations.Write.cs} (95%) rename zero.Core/{Collections/CollectionOperations.cs => Stores/StoreOperations.cs} (96%) diff --git a/zero.Backoffice/Configuration/BackofficeOptions.cs b/zero.Backoffice/Configuration/BackofficeOptions.cs index bf7bccbc..dd7b71da 100644 --- a/zero.Backoffice/Configuration/BackofficeOptions.cs +++ b/zero.Backoffice/Configuration/BackofficeOptions.cs @@ -26,4 +26,9 @@ public class BackofficeOptions /// Configure search maps /// public SearchOptions Search { get; set; } = new(); + + /// + /// Options for configuring the vite development server + /// + public ZeroDevOptions DevServer { get; set; } = new(); } \ No newline at end of file diff --git a/zero.Backoffice/Controllers/ZeroIndexController.cs b/zero.Backoffice/Controllers/ZeroIndexController.cs new file mode 100644 index 00000000..8ac275b8 --- /dev/null +++ b/zero.Backoffice/Controllers/ZeroIndexController.cs @@ -0,0 +1,39 @@ +using Microsoft.AspNetCore.Mvc; + +namespace zero.Backoffice.Controllers; + +[ZeroAuthorize(false)] +[DisableBrowserCache] +public abstract class ZeroIndexController : Controller +{ + IZeroVue ZeroVue { get; set; } + IZeroOptions Options { get; set; } + + public ZeroIndexController(IZeroVue zeroVue, IZeroOptions options) + { + ZeroVue = zeroVue; + Options = options; + } + + + public IActionResult Index() + { + if (Options.Version.IsNullOrEmpty()) + { + return RedirectToAction("ZeroBackoffice", "Setup"); + } + + return View("Views/Zero/Index.cshtml", new ZeroBackofficeModel() + { + Port = Options.For().DevServer.Port, + Vue = ZeroVue + }); + } +} + +public class ZeroBackofficeModel +{ + public int Port { get; set; } + + public IZeroVue Vue { get; set; } +} diff --git a/zero.Backoffice/Controllers/ZeroVueController.cs b/zero.Backoffice/Controllers/ZeroVueController.cs deleted file mode 100644 index 68a23e11..00000000 --- a/zero.Backoffice/Controllers/ZeroVueController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; -using System.Threading.Tasks; -using zero.Core.Identity; - -namespace zero.Web.Controllers -{ - [ZeroAuthorize(false)] - public class ZeroVueController : BackofficeController - { - private IZeroVue ZeroVue { get; set; } - - public ZeroVueController(IZeroVue zeroVue) - { - ZeroVue = zeroVue; - } - - - [HttpGet] - public async Task Config() - { - JsonSerializerSettings settings = new(); - settings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy())); - settings.ContractResolver = new CamelCasePropertyNamesContractResolver(); - settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; - settings.TypeNameHandling = TypeNameHandling.None; - - return new JsonResult(await ZeroVue.Config(), settings); - } - } -} diff --git a/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs b/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs index ff413319..659923ba 100644 --- a/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs +++ b/zero.Backoffice/Controllers/_ZeroBackofficeCollectionController.cs @@ -11,7 +11,7 @@ namespace zero.Backoffice; public abstract class _ZeroBackofficeCollectionController : ZeroBackofficeController where TEntity : ZeroIdEntity, new() - where TCollection : ICollectionOperations + where TCollection : IStoreOperations { protected TCollection Collection { get; private set; } diff --git a/zero.Backoffice/Modules/Countries/CountriesController.cs b/zero.Backoffice/Modules/Countries/CountriesController.cs index 9b4bd176..8c4df0f1 100644 --- a/zero.Backoffice/Modules/Countries/CountriesController.cs +++ b/zero.Backoffice/Modules/Countries/CountriesController.cs @@ -13,9 +13,9 @@ namespace zero.Backoffice.Modules; /// public class CountriesController : ZeroBackofficeApiController { - protected ICountriesCollection Collection { get; set; } + protected ICountriesStore Collection { get; set; } - public CountriesController(ICountriesCollection collection) + public CountriesController(ICountriesStore collection) { Collection = collection; } diff --git a/zero.Backoffice/Modules/Languages/LanguagesController.cs b/zero.Backoffice/Modules/Languages/LanguagesController.cs index b61b656e..386a4c41 100644 --- a/zero.Backoffice/Modules/Languages/LanguagesController.cs +++ b/zero.Backoffice/Modules/Languages/LanguagesController.cs @@ -11,9 +11,9 @@ using zero.Core.Identity; namespace zero.Web.Controllers { [ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Read)] - public class LanguagesController : ZeroBackofficeCollectionController + public class LanguagesController : ZeroBackofficeCollectionController { - public LanguagesController(ILanguagesCollection collection) : base(collection) + public LanguagesController(ILanguagesStore collection) : base(collection) { PreviewTransform = (item, model) => model.Icon = "fth-globe"; } diff --git a/zero.Backoffice/Modules/Mails/MailTemplatesController.cs b/zero.Backoffice/Modules/Mails/MailTemplatesController.cs index 218ba21b..b3b63d2f 100644 --- a/zero.Backoffice/Modules/Mails/MailTemplatesController.cs +++ b/zero.Backoffice/Modules/Mails/MailTemplatesController.cs @@ -8,9 +8,9 @@ using zero.Core.Identity; namespace zero.Web.Controllers { [ZeroAuthorize(Permissions.Settings.Mails, PermissionsValue.Read)] - public class MailTemplatesController : ZeroBackofficeCollectionController + public class MailTemplatesController : ZeroBackofficeCollectionController { - public MailTemplatesController(IMailTemplatesCollection collection) : base(collection) + public MailTemplatesController(IMailTemplatesStore collection) : base(collection) { PreviewTransform = (item, model) => model.Icon = "fth-mail"; } diff --git a/zero.Backoffice/Plugin.cs b/zero.Backoffice/Plugin.cs index f9deaa16..cd4cc83e 100644 --- a/zero.Backoffice/Plugin.cs +++ b/zero.Backoffice/Plugin.cs @@ -1,24 +1,31 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; +using System.IO; namespace zero.Backoffice; -internal class ZeroBackofficePlugin : ZeroPlugin +public class ZeroBackofficePlugin : ZeroPlugin { + internal Action PostConfigureServices = null; + + public ZeroBackofficePlugin() { - Options.Name = "zero.Defaults"; + Options.Name = "zero.Backoffice"; Options.LocalizationPaths.Add("~/Resources/Localization/zero.{lang}.json"); } public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) { - services.AddOptions().Bind(configuration.GetSection("Zero:Backoffice")).Configure(ConfigureOptions); + services.AddOptions().Bind(configuration.GetSection("Zero:Backoffice")).Configure(ConfigureOptions); services.TryAddEnumerable(ServiceDescriptor.Transient, ZeroBackofficeMvcOptions>()); + services.AddHostedService(); + services.AddTransient(); // register all modules ZeroModuleConfiguration moduleConfig = new(services, configuration); @@ -26,12 +33,17 @@ internal class ZeroBackofficePlugin : ZeroPlugin { module.Register(moduleConfig); } + + PostConfigureServices?.Invoke(services, configuration); } - protected void ConfigureOptions(BackofficeOptions options) + protected void ConfigureOptions(BackofficeOptions options, IWebHostEnvironment env) { options.Path = "/zero"; + options.Search.Enabled = true; + options.DevServer.WorkingDirectory = Path.Combine(env.ContentRootPath, "..", "Zero.Web.UI", "App"); + options.IconSets.Add(new BackofficeIconSet() { Alias = "feather", @@ -40,7 +52,6 @@ internal class ZeroBackofficePlugin : ZeroPlugin Prefix = "fth" }); - options.Search.Enabled = true; //Map().Display((x, res, opts) => //{ // PageType pageType = opts.Pages.GetByAlias(x.PageTypeAlias); diff --git a/zero.Backoffice/Registrations.cs b/zero.Backoffice/Registrations.cs index 30496ba3..edcdecab 100644 --- a/zero.Backoffice/Registrations.cs +++ b/zero.Backoffice/Registrations.cs @@ -6,6 +6,7 @@ internal class Registrations { new CountriesModule(), new SearchModule(), - new BackofficeUICompositionModule() + new BackofficeUICompositionModule(), + new BackofficeDevServerModule() }; } \ No newline at end of file diff --git a/zero.Backoffice/ServiceCollectionExtensions.cs b/zero.Backoffice/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..ad4ea2b6 --- /dev/null +++ b/zero.Backoffice/ServiceCollectionExtensions.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Backoffice; + +public static class ServiceCollectionExtensions +{ + public static ZeroBuilder AddBackoffice(this ZeroBuilder builder) where T : ZeroBackofficePlugin, IZeroPlugin, new() + { + return builder.AddBackoffice(); + } + + public static ZeroBuilder AddBackoffice(this ZeroBuilder builder) + { + return builder.AddBackoffice(); + } + + public static ZeroBuilder AddBackoffice(this ZeroBuilder builder, Action options) where T : ZeroBackofficePlugin, IZeroPlugin, new() + { + return builder.AddPlugin(services => + { + T plugin = new T(); + + plugin.PostConfigureServices = (services, configuration) => + { + services.Configure(opts => options(opts)); + }; + + return plugin; + }); + } + + public static ZeroBuilder AddStories(this ZeroBuilder builder, Action options) + { + return builder.AddBackoffice(options); + } +} \ No newline at end of file diff --git a/zero.Backoffice/Usings.cs b/zero.Backoffice/Usings.cs index 3cf5e6e2..c355b1b8 100644 --- a/zero.Backoffice/Usings.cs +++ b/zero.Backoffice/Usings.cs @@ -7,7 +7,7 @@ global using System.Threading.Tasks; global using zero.Applications; global using zero.Architecture; -global using zero.Collections; +global using zero.Stores; global using zero.Configuration; global using zero.Context; global using zero.Extensions; @@ -24,4 +24,5 @@ global using zero.Backoffice.Configuration; global using zero.Backoffice.Controllers; global using zero.Backoffice.Models; global using zero.Backoffice.Modules; -global using zero.Backoffice.UIComposition; \ No newline at end of file +global using zero.Backoffice.UIComposition; +global using zero.Backoffice.DevServer; \ No newline at end of file diff --git a/zero.Backoffice/ZeroVue.cs b/zero.Backoffice/ZeroVue.cs index d232703b..2a03ebea 100644 --- a/zero.Backoffice/ZeroVue.cs +++ b/zero.Backoffice/ZeroVue.cs @@ -20,7 +20,7 @@ using zero.Core.Options; using zero.Core.Plugins; using zero.Web.Models; -namespace zero.Web +namespace zero.Backoffice { public class ZeroVue : IZeroVue { @@ -30,7 +30,7 @@ namespace zero.Web protected IApplicationsApi ApplicationsApi { get; private set; } - protected IAuthenticationApi AuthenticationApi { get; private set; } + protected IAuthenticationService AuthenticationApi { get; private set; } protected IEnumerable Plugins { get; private set; } @@ -43,7 +43,7 @@ namespace zero.Web string IconSymbolsSvg { get; set; } - public ZeroVue(IZeroOptions options, IPaths paths, IApplicationsApi applicationsApi, IAuthenticationApi authenticationApi, IEnumerable plugins, IZeroContext context, ILogger logger, IZeroStore store) + public ZeroVue(IZeroOptions options, IPaths paths, IApplicationsApi applicationsApi, IAuthenticationService authenticationApi, IEnumerable plugins, IZeroContext context, ILogger logger, IZeroStore store) { Paths = paths; Options = options; @@ -73,7 +73,7 @@ namespace zero.Web config.Icons = CreateIconSets(); config.MultiApps = Options.Applications.EnableMultiple; - BackofficeUser user = await AuthenticationApi.GetUser(); + ZeroUser user = await AuthenticationApi.GetUser(); config.Translations = CreateTranslations(user?.LanguageId); diff --git a/zero.Backoffice/_legacy/Controllers/AuthenticationController.cs b/zero.Backoffice/_legacy/Controllers/AuthenticationController.cs index 4b03bafd..702519c8 100644 --- a/zero.Backoffice/_legacy/Controllers/AuthenticationController.cs +++ b/zero.Backoffice/_legacy/Controllers/AuthenticationController.cs @@ -10,16 +10,16 @@ namespace zero.Web.Controllers [ZeroAuthorize(false)] public class AuthenticationController : BackofficeController { - IAuthenticationApi Api; + IAuthenticationService Api; - public AuthenticationController(IAuthenticationApi api) + public AuthenticationController(IAuthenticationService api) { Api = api; } - public async Task> GetUser() => Edit(await Api.GetUser()); + public async Task> GetUser() => Edit(await Api.GetUser()); public EntityResult IsLoggedIn() => EntityResult.Maybe(Api.IsLoggedIn()); diff --git a/zero.Backoffice/_legacy/Controllers/SettingsController.cs b/zero.Backoffice/_legacy/Controllers/SettingsController.cs index 59b38d2d..f09be9c7 100644 --- a/zero.Backoffice/_legacy/Controllers/SettingsController.cs +++ b/zero.Backoffice/_legacy/Controllers/SettingsController.cs @@ -11,12 +11,12 @@ namespace zero.Web.Controllers [ZeroAuthorize(Permissions.Sections.Settings, PermissionsValue.Read)] public class SettingsController : BackofficeController { - IAuthenticationApi AuthApi; + IAuthenticationService AuthApi; IApplicationsApi ApplicationsApi; - public SettingsController(IAuthenticationApi authApi, IApplicationsApi applicationsApi) + public SettingsController(IAuthenticationService authApi, IApplicationsApi applicationsApi) { AuthApi = authApi; ApplicationsApi = applicationsApi; diff --git a/zero.Backoffice/_legacy/Controllers/UserRolesController.cs b/zero.Backoffice/_legacy/Controllers/UserRolesController.cs index 48654796..429e9128 100644 --- a/zero.Backoffice/_legacy/Controllers/UserRolesController.cs +++ b/zero.Backoffice/_legacy/Controllers/UserRolesController.cs @@ -11,31 +11,31 @@ namespace zero.Web.Controllers [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Read)] public class UserRolesController : BackofficeController { - IUserRolesApi Api; - IPermissionsApi PermissionsApi; + IUserRolesService Api; + IPermissionsService PermissionsApi; - public UserRolesController(IUserRolesApi api, IPermissionsApi permissionsApi) + public UserRolesController(IUserRolesService api, IPermissionsService permissionsApi) { Api = api; PermissionsApi = permissionsApi; } - public async Task> GetById([FromQuery] string id) => Edit(await Api.GetById(id)); + public async Task> GetById([FromQuery] string id) => Edit(await Api.GetById(id)); - public async Task> GetAll() => await Api.GetAll(); + public async Task> GetAll() => await Api.GetAll(); public IList GetAllPermissions() => PermissionsApi.GetAll(); [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Save([FromBody] BackofficeUserRole model) => await Api.Save(model); + public async Task> Save([FromBody] ZeroUserRole model) => await Api.Save(model); [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Delete([FromQuery] string id) => await Api.Delete(id); + public async Task> Delete([FromQuery] string id) => await Api.Delete(id); } } diff --git a/zero.Backoffice/_legacy/Controllers/UsersController.cs b/zero.Backoffice/_legacy/Controllers/UsersController.cs index 6cef3e3f..3a68c8a3 100644 --- a/zero.Backoffice/_legacy/Controllers/UsersController.cs +++ b/zero.Backoffice/_legacy/Controllers/UsersController.cs @@ -12,12 +12,12 @@ namespace zero.Web.Controllers [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Read)] public class UsersController : BackofficeController { - IUserApi Api; - IAuthenticationApi AuthenticationApi; - IPermissionsApi PermissionsApi; + IUserService Api; + IAuthenticationService AuthenticationApi; + IPermissionsService PermissionsApi; - public UsersController(IUserApi api, IAuthenticationApi authenticationApi, IPermissionsApi permissionsApi) + public UsersController(IUserService api, IAuthenticationService authenticationApi, IPermissionsService permissionsApi) { Api = api; AuthenticationApi = authenticationApi; @@ -26,10 +26,10 @@ namespace zero.Web.Controllers } - public async Task> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id)); + public async Task> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id)); - public async Task> GetAll([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); + public async Task> GetAll([FromQuery] ListBackofficeQuery query) => await Api.GetByQuery(query); public IList GetAllPermissions() => PermissionsApi.GetAll(); @@ -55,18 +55,18 @@ namespace zero.Web.Controllers [ZeroAuthorize] - public async Task> UpdatePassword([FromBody] UserPasswordEditModel model) + public async Task> UpdatePassword([FromBody] UserPasswordEditModel model) { - EntityResult result; + EntityResult result; if (model.NewPassword != model.ConfirmNewPassword) { - result = EntityResult.Fail(nameof(model.NewPassword), "@errors.changepassword.newpasswordsnotmatching"); + result = EntityResult.Fail(nameof(model.NewPassword), "@errors.changepassword.newpasswordsnotmatching"); } else { - BackofficeUser user = await AuthenticationApi.GetUser(); - result = await Api.UpdatePassword(user as BackofficeUser, model.CurrentPassword, model.NewPassword); + ZeroUser user = await AuthenticationApi.GetUser(); + result = await Api.UpdatePassword(user as ZeroUser, model.CurrentPassword, model.NewPassword); if (result.IsSuccess) { @@ -81,37 +81,37 @@ namespace zero.Web.Controllers [ZeroAuthorize] public async Task> HashPassword([FromBody] UserPasswordEditModel model) { - BackofficeUser user = await Api.GetUserById(model.UserId); + ZeroUser user = await Api.GetUserById(model.UserId); return await Api.HashPassword(user, model.CurrentPassword, model.NewPassword, model.ConfirmNewPassword); } [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Disable([FromBody] BackofficeUser model) + public async Task> Disable([FromBody] ZeroUser model) { - BackofficeUser entity = await Api.GetUserById(model.Id); + ZeroUser entity = await Api.GetUserById(model.Id); return await Api.Disable(entity); } [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Enable([FromBody] BackofficeUser model) + public async Task> Enable([FromBody] ZeroUser model) { - BackofficeUser entity = await Api.GetUserById(model.Id); + ZeroUser entity = await Api.GetUserById(model.Id); return await Api.Enable(entity); } [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Save([FromBody] BackofficeUser model) => await Api.Save(model); + public async Task> Save([FromBody] ZeroUser model) => await Api.Save(model); [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] // TODO do not need settings.users authorization for editing current user profiles - public async Task> SaveCurrent([FromBody] BackofficeUser model) => await Api.Save(model); + public async Task> SaveCurrent([FromBody] ZeroUser model) => await Api.Save(model); [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Delete([FromQuery] string id) => await Api.Delete(id); + public async Task> Delete([FromQuery] string id) => await Api.Delete(id); } } diff --git a/zero.Backoffice/_legacy/Defaults/ZeroBackofficePlugin.cs b/zero.Backoffice/_legacy/Defaults/ZeroBackofficePlugin.cs index 54fece7c..9238c5ab 100644 --- a/zero.Backoffice/_legacy/Defaults/ZeroBackofficePlugin.cs +++ b/zero.Backoffice/_legacy/Defaults/ZeroBackofficePlugin.cs @@ -19,16 +19,16 @@ namespace zero.Web.Defaults //services.AddAll(typeof(IValidator<>), ServiceLifetime.Scoped); //services.AddAll(typeof(IValidator), ServiceLifetime.Scoped); - services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/zero.Backoffice/zero.Backoffice.csproj b/zero.Backoffice/zero.Backoffice.csproj index 5c89cc8e..085120f1 100644 --- a/zero.Backoffice/zero.Backoffice.csproj +++ b/zero.Backoffice/zero.Backoffice.csproj @@ -8,40 +8,6 @@ true - - - - - - - - - - - PreserveNewest - true - PreserveNewest - - - PreserveNewest - true - PreserveNewest - - - PreserveNewest - true - PreserveNewest - - - true - PreserveNewest - - - true - PreserveNewest - - - diff --git a/zero.Core/Applications/ApplicationResolver.cs b/zero.Core/Applications/ApplicationResolver.cs index f07e6890..9f9b3feb 100644 --- a/zero.Core/Applications/ApplicationResolver.cs +++ b/zero.Core/Applications/ApplicationResolver.cs @@ -64,13 +64,13 @@ public class ApplicationResolver : IApplicationResolver /// public async Task ResolveFromUser(ClaimsPrincipal user) { - BackofficeUser userEntity = await GetBackofficeUser(user); + ZeroUser userEntity = await GetBackofficeUser(user); return await ResolveFromUser(userEntity); } /// - public async Task ResolveFromUser(BackofficeUser user) + public async Task ResolveFromUser(ZeroUser user) { if (user == null) { @@ -181,12 +181,12 @@ public class ApplicationResolver : IApplicationResolver /// /// Get backoffice user from claims principal /// - async Task GetBackofficeUser(ClaimsPrincipal user) + async Task GetBackofficeUser(ClaimsPrincipal user) { string userId = user.FindFirstValue(Constants.Auth.Claims.UserId); IAsyncDocumentSession session = Store.Session(global: true); - return await session.LoadAsync(userId); + return await session.LoadAsync(userId); } } @@ -224,5 +224,5 @@ public interface IApplicationResolver /// Resolves the current application from a user. /// This method won't return apps the user has no access to. /// - Task ResolveFromUser(BackofficeUser user); + Task ResolveFromUser(ZeroUser user); } \ No newline at end of file diff --git a/zero.Core/Applications/Module.cs b/zero.Core/Applications/Module.cs deleted file mode 100644 index 9edd3587..00000000 --- a/zero.Core/Applications/Module.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Applications; - -internal class ApplicationsModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/zero.Core/Applications/ServiceCollectionExtensions.cs b/zero.Core/Applications/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..f0a444a8 --- /dev/null +++ b/zero.Core/Applications/ServiceCollectionExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Applications; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroApplications(this IServiceCollection services) + { + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Architecture/Module.cs b/zero.Core/Architecture/Module.cs deleted file mode 100644 index ff6163d2..00000000 --- a/zero.Core/Architecture/Module.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Applications; - -internal class ArchitectureModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/zero.Core/Architecture/Modules/ZeroModule.cs b/zero.Core/Architecture/Modules/ZeroModule.cs deleted file mode 100644 index fe602b34..00000000 --- a/zero.Core/Architecture/Modules/ZeroModule.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace zero.Architecture; - -public class ZeroModule -{ - public virtual void Register(IZeroModuleConfiguration config) { } - - public virtual void Configure(IZeroOptions options) { } -} \ No newline at end of file diff --git a/zero.Core/Architecture/Modules/ZeroModuleConfiguration.cs b/zero.Core/Architecture/Modules/ZeroModuleConfiguration.cs deleted file mode 100644 index 6f8267b8..00000000 --- a/zero.Core/Architecture/Modules/ZeroModuleConfiguration.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Architecture; - -public class ZeroModuleConfiguration : IZeroModuleConfiguration -{ - public IServiceCollection Services { get; } - - public IConfiguration Configuration { get; } - - public ZeroModuleConfiguration(IServiceCollection servicse, IConfiguration configuration) - { - Services = servicse; - Configuration = configuration; - } -} - - -public interface IZeroModuleConfiguration -{ - IServiceCollection Services { get; } - - IConfiguration Configuration { get; } -} diff --git a/zero.Core/Architecture/Modules/ZeroModuleInitializer.cs b/zero.Core/Architecture/Modules/ZeroModuleInitializer.cs deleted file mode 100644 index 6e95139d..00000000 --- a/zero.Core/Architecture/Modules/ZeroModuleInitializer.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace zero.Architecture; - -internal class ZeroModuleInitializer -{ - public static void RegisterAll(IZeroModuleConfiguration config) - { - foreach (ZeroModule module in Registrations.Modules) - { - module.Register(config); - } - } - - public static void ConfigureAll(IZeroOptions options) - { - foreach (ZeroModule module in Registrations.Modules) - { - module.Configure(options); - } - } -} \ No newline at end of file diff --git a/zero.Core/Architecture/ServiceCollectionExtensions.cs b/zero.Core/Architecture/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..6a3a0f9a --- /dev/null +++ b/zero.Core/Architecture/ServiceCollectionExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Architecture; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroBlueprints(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Collections/CountriesCollection.cs b/zero.Core/Collections/CountriesCollection.cs deleted file mode 100644 index 62b2aa80..00000000 --- a/zero.Core/Collections/CountriesCollection.cs +++ /dev/null @@ -1,18 +0,0 @@ -using FluentValidation; - -namespace zero.Collections; - -public class CountriesCollection : CachableEntityCollection, ICountriesCollection -{ - public CountriesCollection(ICollectionContext context, ICollectionCache cache) : base(context, cache) { } - - /// - protected override void ValidationRules(ZeroValidator validator) - { - validator.RuleFor(x => x.Code).Length(2).Unique(Session); - validator.RuleFor(x => x.Name).Length(2, 120); - } -} - - -public interface ICountriesCollection : IEntityCollection { } \ No newline at end of file diff --git a/zero.Core/Communication/Module.cs b/zero.Core/Communication/Module.cs deleted file mode 100644 index 5e70c9e4..00000000 --- a/zero.Core/Communication/Module.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Applications; - -internal class CommunicationModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddSingleton(); - config.Services.AddTransient(); - } -} \ No newline at end of file diff --git a/zero.Core/Communication/ServiceCollectionExtensions.cs b/zero.Core/Communication/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..7f08b1a4 --- /dev/null +++ b/zero.Core/Communication/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Communication; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroCommunication(this IServiceCollection services) + { + services.AddScoped(); + services.AddSingleton(); + services.AddTransient(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Configuration/Integrations/IntegrationService.cs b/zero.Core/Configuration/Integrations/IntegrationService.cs index 6fc81362..b48f888b 100644 --- a/zero.Core/Configuration/Integrations/IntegrationService.cs +++ b/zero.Core/Configuration/Integrations/IntegrationService.cs @@ -4,7 +4,7 @@ namespace zero.Configuration; public class IntegrationService : IntegrationsCollection, IIntegrationService { - public IntegrationService(ICollectionContext context, ILogger logger) : base(context, logger) + public IntegrationService(IStoreContext context, ILogger logger) : base(context, logger) { Options = new(false); } diff --git a/zero.Core/Configuration/Module.cs b/zero.Core/Configuration/Module.cs deleted file mode 100644 index 423fd347..00000000 --- a/zero.Core/Configuration/Module.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace zero.Configuration; - -internal class ConfigurationModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddOptions().Bind(config.Configuration.GetSection("Zero")).Configure(opts => { }); - config.Services.AddTransient(factory => factory.GetService>().CurrentValue); - } -} \ No newline at end of file diff --git a/zero.Core/Configuration/ServiceCollectionExtensions.cs b/zero.Core/Configuration/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..dbc3d293 --- /dev/null +++ b/zero.Core/Configuration/ServiceCollectionExtensions.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace zero.Configuration; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroConfiguration(this IServiceCollection services, IConfiguration config) + { + services.AddOptions().Bind(config.GetSection("Zero")).Configure(opts => { }); + services.AddTransient(factory => factory.GetService>().CurrentValue); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Context/Module.cs b/zero.Core/Context/Module.cs deleted file mode 100644 index 0d0bf81c..00000000 --- a/zero.Core/Context/Module.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Applications; - -internal class ContextModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddHttpContextAccessor(); - } -} \ No newline at end of file diff --git a/zero.Core/Context/ServiceCollectionExtensions.cs b/zero.Core/Context/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..ec97450b --- /dev/null +++ b/zero.Core/Context/ServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Context; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroContext(this IServiceCollection services) + { + services.AddScoped(); + services.AddHttpContextAccessor(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/FileStorage/ServiceCollectionExtensions.cs b/zero.Core/FileStorage/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..82ac759b --- /dev/null +++ b/zero.Core/FileStorage/ServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; + +namespace zero.FileStorage; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroFileStorage(this IServiceCollection services) + { + services.AddScoped(factory => new Paths(factory.GetService(), true)); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Identity/Api/AuthenticationApi.cs b/zero.Core/Identity/Api/AuthenticationApi.cs deleted file mode 100644 index 4ec2e558..00000000 --- a/zero.Core/Identity/Api/AuthenticationApi.cs +++ /dev/null @@ -1,228 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Raven.Client.Documents; -using Raven.Client.Documents.Session; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using zero.Core.Database; -using zero.Core.Entities; -using zero.Core.Extensions; -using zero.Core.Identity; - -namespace zero.Core.Api -{ - public class AuthenticationApi : IAuthenticationApi - { - protected IZeroContext Context { get; set; } - - protected SignInManager SignInManager { get; private set; } - - protected IZeroStore Store { get; set; } - - - public AuthenticationApi(IZeroContext context, SignInManager signInManager, IZeroStore store) - { - Context = context; - SignInManager = signInManager; - Store = store; - } - - - /// - public bool IsLoggedIn() - { - return SignInManager.IsSignedIn(Context.BackofficeUser); - } - - - /// - public bool IsSuper() - { - return Context.BackofficeUser.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); - } - - - /// - public bool IsAdmin() - { - return Context.BackofficeUser.HasClaim(Constants.Auth.Claims.Role, "administrator"); // TODO use constant (in setup as well) - } - - - /// - public async Task GetUser() - { - return await SignInManager.UserManager.GetUserAsync(Context.BackofficeUser); - } - - - /// - public IList GetPermissions(string prefix = null) - { - return Context.BackofficeUser.Claims - .Where(claim => claim.Type == Constants.Auth.Claims.Permission && (prefix == null || claim.Value.StartsWith(prefix))) - .Select(claim => Permission.FromClaim(claim, prefix)) - .ToList(); - } - - - /// - public Permission GetPermission(string key = null) - { - Claim claim = Context.BackofficeUser.Claims.FirstOrDefault(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(key + ":")); - return Permission.FromClaim(claim); - } - - - /// - public async Task Login(string email, string password, bool isPersistent) - { - EntityResult result = new EntityResult(); - - BackofficeUser user = await SignInManager.UserManager.FindByNameAsync(email); - - if (user == null) - { - result.AddError("@login.errors.wrongcredentials"); // TODO we don't need translations here, but return an enum, so the app itself can translate the error - return result; - } - // TODO probably move this logic into a custom SignInManager which overrides CanSignInAsync() - // see https://stackoverflow.com/a/35484758/670860 - else if (!user.IsActive) - { - result.AddError("@login.errors.disabled"); - return result; - } - - SignInResult signInResult = await SignInManager.PasswordSignInAsync(user, password, isPersistent, true); - - if (!signInResult.Succeeded) - { - if (signInResult.IsLockedOut) - { - result.AddError("@login.errors.lockedout"); - } - else if (signInResult.IsNotAllowed) - { - result.AddError("@login.errors.notallowed"); - } - else if (signInResult.RequiresTwoFactor) - { - result.AddError("@login.errors.requirestwofactor"); - } - else - { - result.AddError("@login.errors.wrongcredentials"); - } - - return result; - } - - return EntityResult.Success(); - } - - - /// - public async Task Logout() - { - await SignInManager.SignOutAsync(); - } - - - /// - public string GetUserId() - { - return SignInManager.UserManager.GetUserId(Context.BackofficeUser); - } - - - /// - public async Task TrySwitchApp(string appId) - { - IZeroDocumentSession session = Store.Session(global: true); - BackofficeUser user = await GetUser(); - - if (user == null || appId.IsNullOrEmpty()) - { - return false; - } - - string[] allowedAppIds = user.GetAllowedAppIds(); - - 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 - - await session.StoreAsync(user); - await session.SaveChangesAsync(); - - return true; - } - - return false; - } - } - - - public interface IAuthenticationApi - { - /// - /// Get currently logged-in user - /// - Task GetUser(); - - /// - /// Whether a user is currently logged-in - /// - bool IsLoggedIn(); - - /// - /// Whether the current user is the super user who created the zero instance - /// - bool IsSuper(); - - /// - /// Whether the current user belongs to the administrator role (will always return false if this role gets deleted) - /// - bool IsAdmin(); - - /// - /// Logs a zero-user in and sets cookie - /// - Task Login(string email, string password, bool isPersistent); - - /// - /// Logs out the current user - /// - Task Logout(); - - /// - /// Get the ID of the currently logged in user - /// - string GetUserId(); - - /// - /// Get all permissions for the current user with an optional prefix - /// - IList GetPermissions(string prefix = null); - - /// - /// Get a single permissions by key - /// - Permission GetPermission(string key = null); - - /// - /// Tries to switch the currently loaded backoffice application for the current user - /// - Task TrySwitchApp(string appId); - } -} diff --git a/zero.Core/Identity/Api/AuthorizationApi.cs b/zero.Core/Identity/Api/AuthorizationApi.cs deleted file mode 100644 index 74740c81..00000000 --- a/zero.Core/Identity/Api/AuthorizationApi.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Raven.Client.Documents; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using zero.Core.Database; -using zero.Core.Entities; -using zero.Core.Identity; - -namespace zero.Core.Api -{ - public class AuthorizationApi : IAuthorizationApi - { - protected IHttpContextAccessor HttpContextAccessor { get; set; } - - protected SignInManager SignInManager { get; private set; } - - protected ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; - - - public AuthorizationApi(IHttpContextAccessor httpContextAccessor, SignInManager signInManager) - { - HttpContextAccessor = httpContextAccessor; - SignInManager = signInManager; - } - - - /// - public bool IsLoggedIn() - { - ClaimsPrincipal principal = HttpContextAccessor.HttpContext.User; - - bool isAuthenticated = principal.Identity.IsAuthenticated; - bool isZeroUser = principal.HasClaim(Constants.Auth.Claims.IsZero, PermissionsValue.True); - - bool isSignedIn = SignInManager.IsSignedIn(principal); - - return isAuthenticated && isZeroUser && isSignedIn; - } - - - /// - public bool IsSuper() - { - return Principal.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); - } - - - /// - public bool IsAdmin() - { - return Principal.HasClaim(Constants.Auth.Claims.Role, "administrator"); // TODO use constant (in setup as well) - } - - - /// - public IList GetPermissions(string prefix = null) - { - return Principal.Claims - .Where(claim => claim.Type == Constants.Auth.Claims.Permission && (prefix == null || claim.Value.StartsWith(prefix))) - .Select(claim => Permission.FromClaim(claim, prefix)) - .ToList(); - } - - - /// - public Permission GetPermission(string key = null) - { - Claim claim = Principal.Claims.FirstOrDefault(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(key + ":")); - - return Permission.FromClaim(claim); - } - - - public EntityPermission GetPermissionForEntity(T model, string permissionKey) where T : ZeroEntity - { - EntityPermission result = new EntityPermission(); - - if (!IsLoggedIn()) - { - return result; - } - - Type type = typeof(T); - bool isSuperUser = Principal.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); - - //result.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx - //result.IsShareable = result.IsAppAware && AppAwareShareableType.IsAssignableFrom(type); - - if (isSuperUser) - { - result.CanCreate = true; - result.CanCreateShared = result.CanCreate && result.IsShareable; - result.CanEdit = true; - result.CanRead = true; - result.CanDelete = true; - return result; - } - - Permission permission = GetPermission(permissionKey); - - if (permission != null) - { - - } - - return result; - } - } - - - public interface IAuthorizationApi - { - /// - /// Whether a user is currently logged-in - /// - bool IsLoggedIn(); - - /// - /// Whether the current user is the super user who created the zero instance - /// - bool IsSuper(); - - /// - /// Whether the current user belongs to the administrator role (will always return false if this role gets deleted) - /// - bool IsAdmin(); - - /// - /// Get all permissions for the current user with an optional prefix - /// - IList GetPermissions(string prefix = null); - - /// - /// Get a single permissions by key - /// - public Permission GetPermission(string key = null); - } -} diff --git a/zero.Core/Identity/Api/PermissionsApi.cs b/zero.Core/Identity/Api/PermissionsApi.cs deleted file mode 100644 index a75bad96..00000000 --- a/zero.Core/Identity/Api/PermissionsApi.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using zero.Core.Entities; -using zero.Core.Identity; -using zero.Core.Options; - -namespace zero.Core.Api -{ - public class PermissionsApi : IPermissionsApi - { - protected IZeroOptions Options { get; set; } - - public PermissionsApi(IZeroOptions options) - { - Options = options; - } - - - /// - public IList GetAll() - { - List result = Options.Permissions.GetAllItems().ToList(); - PermissionCollection spaceCollection = result.FirstOrDefault(x => x.Alias == Constants.PermissionCollections.Spaces); - PermissionCollection moduleCollection = result.FirstOrDefault(x => x.Alias == Constants.PermissionCollections.Modules); - - if (spaceCollection != null) - { - spaceCollection.Items.Clear(); - - foreach (Space space in Options.Spaces.GetAllItems()) - { - spaceCollection.Items.Add(new Permission(Permissions.Spaces.PREFIX + space.Alias, space.Name, null, PermissionValueType.CRUD)); - } - } - - if (moduleCollection != null) - { - moduleCollection.Items.Clear(); - - foreach (ModuleType module in Options.Modules.GetAllItems()) - { - moduleCollection.Items.Add(new Permission(Permissions.Modules.PREFIX + module.Alias, module.Name, module.Description, PermissionValueType.CRUD)); - } - } - - return result; - } - } - - - public interface IPermissionsApi - { - /// - /// Get all available permissions to choose from - /// - IList GetAll(); - } -} diff --git a/zero.Core/Identity/AuthenticationBuilderExtensions.cs b/zero.Core/Identity/AuthenticationBuilderExtensions.cs index 18c26b93..89d0eba1 100644 --- a/zero.Core/Identity/AuthenticationBuilderExtensions.cs +++ b/zero.Core/Identity/AuthenticationBuilderExtensions.cs @@ -9,8 +9,8 @@ namespace zero.Identity; public static class AuthenticationBuilderExtensions { public static AuthenticationBuilder AddZeroBackofficeCookie(this AuthenticationBuilder builder, Action> setupAction = null) - where TUser : BackofficeUser - where TRole : BackofficeUserRole + where TUser : ZeroUser + where TRole : ZeroUserRole { return builder.AddCookie(Constants.Auth.BackofficeScheme, Constants.Auth.BackofficeDisplayName, true, b => { diff --git a/zero.Core/Identity/BackofficeUserExtensions.cs b/zero.Core/Identity/BackofficeUserExtensions.cs index 74f4d0be..b11b67de 100644 --- a/zero.Core/Identity/BackofficeUserExtensions.cs +++ b/zero.Core/Identity/BackofficeUserExtensions.cs @@ -2,7 +2,7 @@ public static class BackofficeUserExtensions { - public static string[] GetAllowedAppIds(this BackofficeUser user) + public static string[] GetAllowedAppIds(this ZeroUser user) { if (user == null) { diff --git a/zero.Core/Identity/Collections/UserApi.cs b/zero.Core/Identity/Collections/UserApi.cs deleted file mode 100644 index b09e6f0a..00000000 --- a/zero.Core/Identity/Collections/UserApi.cs +++ /dev/null @@ -1,283 +0,0 @@ -using Microsoft.AspNetCore.Identity; -using Raven.Client.Documents; -using Raven.Client.Documents.Linq; -using Raven.Client.Documents.Session; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using zero.Core.Collections; -using zero.Core.Entities; -using zero.Core.Extensions; - -namespace zero.Core.Api -{ - public class UserApi : BackofficeApi, IUserApi - { - protected UserManager UserManager { get; private set; } - - public UserApi(ICollectionContext store, UserManager userManager) : base(store, isCoreDatabase: true) - { - UserManager = userManager; - } - - - /// - public async Task GetUserById(string id) - { - BackofficeUser user = await UserManager.FindByIdAsync(id); - return user; - } - - - /// - public async Task GetUserByEmail(string email) - { - BackofficeUser user = await UserManager.FindByEmailAsync(email); - return user; - } - - - /// - public async Task> GetByIds(params string[] ids) - { - return await GetByIds(ids); - } - - - /// - public async Task> GetAll() - { - return await Session.Query() - .OrderByDescending(x => x.CreatedDate) - .ToListAsync(); - } - - - /// - public async Task> GetByQuery(ListQuery query) - { - string currentUserId = UserManager.GetUserId(Context.Context.BackofficeUser); - - query.SearchSelector = user => user.Name; - - return await Session.Query() - .ToQueriedListAsync(query); - } - - - /// - public async Task> Save(BackofficeUser model) - { - bool updateSecurityStamp = false; - - if (!model.Id.IsNullOrEmpty()) - { - BackofficeUser origin = await GetUserById(model.Id); - updateSecurityStamp = origin != null && model.PasswordHash != origin.PasswordHash; - } - - EntityResult result = await SaveModel(model); //, new UserValidator()); - - if (updateSecurityStamp) - { - await UserManager.UpdateSecurityStampAsync(model); - } - - return result; - } - - - /// - public async Task> Delete(string id) - { - return await DeleteById(id); - } - - - /// - public async Task> HashPassword(BackofficeUser user, string currentPassword, string newPassword, string confirmNewPassword) - { - if (newPassword != confirmNewPassword) - { - return EntityResult.Fail(nameof(newPassword), "@errors.changepassword.newpasswordsnotmatching"); - } - - if (currentPassword.IsNullOrWhiteSpace() || newPassword.IsNullOrWhiteSpace()) - { - return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); - } - - if (user == null) - { - return EntityResult.Fail("@errors.changepassword.nouser"); - } - - if (UserManager.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, currentPassword) != PasswordVerificationResult.Success) - { - return EntityResult.Fail("@errors.changepassword.passwordincorrect"); - } - - // validate new password - List errors = new(); - bool isValid = true; - foreach (var v in UserManager.PasswordValidators) - { - var result = await v.ValidateAsync(UserManager, user, newPassword); - if (!result.Succeeded) - { - if (result.Errors.Any()) - { - errors.AddRange(result.Errors); - } - - isValid = false; - } - } - - if (!isValid) - { - EntityResult result = EntityResult.Fail(); - foreach (IdentityError error in errors) - { - result.AddError(error.Description); - } - return result; - } - - return EntityResult.Success(UserManager.PasswordHasher.HashPassword(user, newPassword)); - } - - - /// - public async Task> UpdatePassword(BackofficeUser user, string currentPassword, string newPassword) - { - if (currentPassword.IsNullOrWhiteSpace() || newPassword.IsNullOrWhiteSpace()) - { - return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); - } - - if (user == null) - { - return EntityResult.Fail("@errors.changepassword.nouser"); - } - - IdentityResult identityResult = await UserManager.ChangePasswordAsync(user as BackofficeUser, currentPassword, newPassword); - - if (!identityResult.Succeeded) - { - EntityResult result = EntityResult.Fail(); - - foreach (IdentityError error in identityResult.Errors) - { - result.AddError(error.Description); - } - - return result; - } - - return EntityResult.Success(user); - } - - - /// - public async Task> Enable(BackofficeUser user) - { - return await UpdateActiveState(user, true); - } - - - /// - public async Task> Disable(BackofficeUser user) - { - return await UpdateActiveState(user, false); - } - - - /// - /// Updates the active state of user. - /// If IsActive=false, the user cannot login anymore - /// - async Task> UpdateActiveState(BackofficeUser user, bool isActive) - { - user.IsActive = isActive; - - IdentityResult identityResult = await UserManager.UpdateAsync(user as BackofficeUser); - - if (!identityResult.Succeeded) - { - EntityResult result = EntityResult.Fail(); - - foreach (IdentityError error in identityResult.Errors) - { - result.AddError(error.Description); - } - - return result; - } - - await UserManager.UpdateSecurityStampAsync(user as BackofficeUser); - - return EntityResult.Success(user); - } - } - - - public interface IUserApi : IBackofficeApi - { - /// - /// Find user by id - /// - Task GetUserById(string id); - - /// - /// Find user by email - /// - Task GetUserByEmail(string email); - - /// - /// Get users by ids - /// - Task> GetByIds(params string[] ids); - - /// - /// Get all users for the selected application - /// - Task> GetAll(); - - /// - /// Get all available users (with query) - /// - Task> GetByQuery(ListQuery query); - - /// - /// Creates or updates a user - /// - Task> Save(BackofficeUser model); - - /// - /// Deletes a user - /// - Task> Delete(string id); - - /// - /// Changes the password of the current user. - /// User is logged out if this operation succeeds. - /// - Task> UpdatePassword(BackofficeUser user, string currentPassword, string newPassword); - - /// - /// Tries to hash a new password - /// - Task> HashPassword(BackofficeUser user, string currentPassword, string newPassword, string confirmNewPassword); - - /// - /// Enables a user - /// - Task> Enable(BackofficeUser user); - - /// - /// Disables a user - /// - Task> Disable(BackofficeUser user); - } -} diff --git a/zero.Core/Identity/Collections/UserRolesApi.cs b/zero.Core/Identity/Collections/UserRolesApi.cs deleted file mode 100644 index 8a6c6317..00000000 --- a/zero.Core/Identity/Collections/UserRolesApi.cs +++ /dev/null @@ -1,125 +0,0 @@ -using FluentValidation.Results; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Identity; -using Raven.Client.Documents; -using Raven.Client.Documents.Session; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using zero.Core.Collections; -using zero.Core.Entities; -using zero.Core.Extensions; -using zero.Core.Identity; -using zero.Core.Validation; - -namespace zero.Core.Api -{ - public class UserRolesApi : BackofficeApi, IUserRolesApi - { - protected IHttpContextAccessor HttpContextAccessor { get; set; } - - protected UserManager UserManager { get; private set; } - - protected RoleManager RoleManager { get; private set; } - - private ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; - - - public UserRolesApi(IHttpContextAccessor httpContextAccessor, UserManager userManager, RoleManager roleManager, ICollectionContext store) : base(store, isCoreDatabase: true) - { - HttpContextAccessor = httpContextAccessor; - UserManager = userManager; - RoleManager = roleManager; - } - - - /// - public async Task> GetAll() - { - return await Session.Query().OrderBy(x => x.Sort).ThenBy(x => x.Name).ToListAsync(); - } - - - /// - public async Task GetById(string id) - { - return await RoleManager.FindByIdAsync(id); - } - - - /// - public async Task> Save(BackofficeUserRole model) - { - ValidationResult validation = await new UserRoleValidator().ValidateAsync(model); - - if (!validation.IsValid) - { - return EntityResult.Fail(validation); - } - - if (model.Id.IsNullOrEmpty()) - { - model.CreatedDate = DateTimeOffset.Now; - } - - model.Alias = Safenames.Alias(model.Name); - - await Session.StoreAsync(model); - - string id = Session.Advanced.GetDocumentId(model); - - await Session.SaveChangesAsync(); - - if (model.Id.IsNullOrEmpty()) - { - model.Id = id; - } - - return EntityResult.Success(model); - } - - - /// - public async Task> Delete(string id) - { - BackofficeUserRole country = await Session.LoadAsync(id); - - if (country == null) - { - return EntityResult.Fail("@errors.ondelete.idnotfound"); - } - - Session.Delete(country); - - await Session.SaveChangesAsync(); - - return EntityResult.Success(); - } - } - - - public interface IUserRolesApi : IBackofficeApi - { - /// - /// Get all user roles - /// - Task> GetAll(); - - /// - /// Get role by id - /// - Task GetById(string id); - - /// - /// Create or update a role - /// - Task> Save(BackofficeUserRole model); - - /// - /// Deletes a role - /// - Task> Delete(string id); - } -} diff --git a/zero.Core/Identity/IdentityModule.cs b/zero.Core/Identity/IdentityModule.cs deleted file mode 100644 index 6b176025..00000000 --- a/zero.Core/Identity/IdentityModule.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Authentication.Cookies; -using Microsoft.AspNetCore.Identity; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - -namespace zero.Identity; - -internal class IdentityModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureCookieAuthenticationOptions>()); - - config.Services.AddZeroIdentity(); - config.Services.Replace, ZeroBackofficeClaimsPrincipalFactory>(); - config.Services.Replace, RavenCoreUserStore>(ServiceLifetime.Scoped); - config.Services.Replace, RavenCoreRoleStore>(ServiceLifetime.Scoped); - - config.Services.AddAuthentication(Constants.Auth.BackofficeScheme); - //.AddZeroBackofficeCookie(); // TODO - - config.Services.AddAuthorization(); - } - - /// - public override void Configure(IZeroOptions options) - { - - } -} \ No newline at end of file diff --git a/zero.Core/Identity/Models/UserClaim.cs b/zero.Core/Identity/Models/UserClaim.cs index 523e4b76..ed3aad62 100644 --- a/zero.Core/Identity/Models/UserClaim.cs +++ b/zero.Core/Identity/Models/UserClaim.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Security.Claims; +using System.Security.Claims; namespace zero.Identity; @@ -18,7 +17,7 @@ public class UserClaim /// /// Convert to a claim /// - public Claim ToClaim() => new Claim(Type, Value); + public Claim ToClaim() => new(Type, Value); public UserClaim() { } @@ -39,19 +38,4 @@ public class UserClaim Type = claim?.Type; Value = claim?.Value; } -} - - - -public class UserClaimComparer : IEqualityComparer -{ - public bool Equals(UserClaim x, UserClaim y) - { - return (x == null && y == null) || (x.Type.Equals(y.Type, StringComparison.InvariantCultureIgnoreCase) && x.Value.Equals(y.Value, StringComparison.InvariantCultureIgnoreCase)); - } - - public int GetHashCode(UserClaim obj) - { - return (obj.Type + obj.Value).GetHashCode(); - } -} +} \ No newline at end of file diff --git a/zero.Core/Identity/Models/UserClaimComparer.cs b/zero.Core/Identity/Models/UserClaimComparer.cs new file mode 100644 index 00000000..8765ff60 --- /dev/null +++ b/zero.Core/Identity/Models/UserClaimComparer.cs @@ -0,0 +1,14 @@ +namespace zero.Identity; + +public class UserClaimComparer : IEqualityComparer +{ + public bool Equals(UserClaim x, UserClaim y) + { + return (x == null && y == null) || (x.Type.Equals(y.Type, StringComparison.InvariantCultureIgnoreCase) && x.Value.Equals(y.Value, StringComparison.InvariantCultureIgnoreCase)); + } + + public int GetHashCode(UserClaim obj) + { + return (obj.Type + obj.Value).GetHashCode(); + } +} diff --git a/zero.Core/Identity/Models/ZeroIdentityRole.cs b/zero.Core/Identity/Models/ZeroIdentityRole.cs index 8a1cf5e8..0f78fbbb 100644 --- a/zero.Core/Identity/Models/ZeroIdentityRole.cs +++ b/zero.Core/Identity/Models/ZeroIdentityRole.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace zero.Identity; +namespace zero.Identity; public abstract class ZeroIdentityRole : ZeroEntity { diff --git a/zero.Core/Identity/Models/ZeroIdentityUser.cs b/zero.Core/Identity/Models/ZeroIdentityUser.cs index 96863743..c29dea33 100644 --- a/zero.Core/Identity/Models/ZeroIdentityUser.cs +++ b/zero.Core/Identity/Models/ZeroIdentityUser.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace zero.Identity; +namespace zero.Identity; public abstract class ZeroIdentityUser : ZeroEntity { diff --git a/zero.Core/Identity/Models/BackofficeUser.cs b/zero.Core/Identity/Models/ZeroUser.cs similarity index 74% rename from zero.Core/Identity/Models/BackofficeUser.cs rename to zero.Core/Identity/Models/ZeroUser.cs index 513787e3..f06444c4 100644 --- a/zero.Core/Identity/Models/BackofficeUser.cs +++ b/zero.Core/Identity/Models/ZeroUser.cs @@ -1,7 +1,10 @@ namespace zero.Identity; +/// +/// A zero user can access the zero API and backoffice by granting the necessary permissions +/// [RavenCollection("Users")] -public class BackofficeUser : ZeroIdentityUser +public class ZeroUser : ZeroIdentityUser { /// /// Application the user registered in diff --git a/zero.Core/Identity/Models/BackofficeUserRole.cs b/zero.Core/Identity/Models/ZeroUserRole.cs similarity index 84% rename from zero.Core/Identity/Models/BackofficeUserRole.cs rename to zero.Core/Identity/Models/ZeroUserRole.cs index 542d5551..930cb8d0 100644 --- a/zero.Core/Identity/Models/BackofficeUserRole.cs +++ b/zero.Core/Identity/Models/ZeroUserRole.cs @@ -1,7 +1,7 @@ namespace zero.Identity; [RavenCollection("Roles")] -public class BackofficeUserRole : ZeroIdentityRole +public class ZeroUserRole : ZeroIdentityRole { /// /// Additional description diff --git a/zero.Core/Identity/RavenRoleStore(TRole).cs b/zero.Core/Identity/RavenRoleStore(TRole).cs index 38329af4..5139d1eb 100644 --- a/zero.Core/Identity/RavenRoleStore(TRole).cs +++ b/zero.Core/Identity/RavenRoleStore(TRole).cs @@ -2,10 +2,7 @@ using Raven.Client.Documents; using Raven.Client.Documents.Linq; using Raven.Client.Exceptions; -using System.Collections.Generic; using System.Security.Claims; -using System.Threading; -using System.Threading.Tasks; namespace zero.Identity; diff --git a/zero.Core/Identity/RavenUserStore(TUser).cs b/zero.Core/Identity/RavenUserStore(TUser).cs index 49b98e5e..27f66d50 100644 --- a/zero.Core/Identity/RavenUserStore(TUser).cs +++ b/zero.Core/Identity/RavenUserStore(TUser).cs @@ -2,11 +2,7 @@ using Raven.Client.Documents; using Raven.Client.Documents.Linq; using Raven.Client.Documents.Session; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Threading; -using System.Threading.Tasks; namespace zero.Identity; diff --git a/zero.Core/Identity/RavenUserStore(TUser,TRole).cs b/zero.Core/Identity/RavenUserStore(TUser,TRole).cs index 7a1d45b3..6ee0791a 100644 --- a/zero.Core/Identity/RavenUserStore(TUser,TRole).cs +++ b/zero.Core/Identity/RavenUserStore(TUser,TRole).cs @@ -1,7 +1,6 @@ using Microsoft.AspNetCore.Identity; using Raven.Client.Documents; using Raven.Client.Documents.Linq; -using System.Linq; namespace zero.Identity; diff --git a/zero.Core/Identity/Security/SchemedSignInManager.cs b/zero.Core/Identity/Security/SchemedSignInManager.cs index 775a0e89..d9c6f0c3 100644 --- a/zero.Core/Identity/Security/SchemedSignInManager.cs +++ b/zero.Core/Identity/Security/SchemedSignInManager.cs @@ -3,10 +3,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Threading.Tasks; namespace zero.Identity; diff --git a/zero.Core/Identity/Security/ZeroAuthorizeAttribute.cs b/zero.Core/Identity/Security/ZeroAuthorizeAttribute.cs index d2370f9f..8cd27988 100644 --- a/zero.Core/Identity/Security/ZeroAuthorizeAttribute.cs +++ b/zero.Core/Identity/Security/ZeroAuthorizeAttribute.cs @@ -1,7 +1,5 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; namespace zero.Identity; diff --git a/zero.Core/Identity/Security/ZeroBackofficeClaimsPrincipalFactory.cs b/zero.Core/Identity/Security/ZeroBackofficeClaimsPrincipalFactory.cs index 1cb61b12..636fd5c5 100644 --- a/zero.Core/Identity/Security/ZeroBackofficeClaimsPrincipalFactory.cs +++ b/zero.Core/Identity/Security/ZeroBackofficeClaimsPrincipalFactory.cs @@ -1,15 +1,12 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Threading.Tasks; namespace zero.Identity; public class ZeroBackofficeClaimsPrincipalFactory : ZeroClaimsPrinicipalFactory - where TUser : BackofficeUser - where TRole : BackofficeUserRole + where TUser : ZeroUser + where TRole : ZeroUserRole { public ZeroBackofficeClaimsPrincipalFactory(UserManager userManager, RoleManager roleManager, IOptions optionsAccessor, IOptions> authOptions, IZeroContext zero) : base(userManager, roleManager, optionsAccessor, authOptions, zero) diff --git a/zero.Core/Identity/Security/ZeroClaimsPrinicipalFactory.cs b/zero.Core/Identity/Security/ZeroClaimsPrinicipalFactory.cs index 52a2f1d8..7babb1b5 100644 --- a/zero.Core/Identity/Security/ZeroClaimsPrinicipalFactory.cs +++ b/zero.Core/Identity/Security/ZeroClaimsPrinicipalFactory.cs @@ -1,8 +1,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; -using System.Collections.Generic; using System.Security.Claims; -using System.Threading.Tasks; namespace zero.Identity; diff --git a/zero.Core/Identity/ServiceCollectionExtensions.cs b/zero.Core/Identity/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..76ff8c85 --- /dev/null +++ b/zero.Core/Identity/ServiceCollectionExtensions.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace zero.Identity; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroIdentity(this IServiceCollection services) + { + services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureCookieAuthenticationOptions>()); + + services.AddZeroIdentity(); + services.Replace, ZeroBackofficeClaimsPrincipalFactory>(); + services.Replace, RavenCoreUserStore>(ServiceLifetime.Scoped); + services.Replace, RavenCoreRoleStore>(ServiceLifetime.Scoped); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddAuthentication(Constants.Auth.BackofficeScheme); + //.AddZeroBackofficeCookie(); // TODO + + services.AddAuthorization(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Identity/Services/AuthenticationService.cs b/zero.Core/Identity/Services/AuthenticationService.cs new file mode 100644 index 00000000..403b26cf --- /dev/null +++ b/zero.Core/Identity/Services/AuthenticationService.cs @@ -0,0 +1,218 @@ +using Microsoft.AspNetCore.Identity; + +namespace zero.Identity; + +public class AuthenticationService : IAuthenticationService +{ + protected IZeroContext Context { get; set; } + + protected SignInManager SignInManager { get; private set; } + + protected IZeroStore Store { get; set; } + + + public AuthenticationService(IZeroContext context, SignInManager signInManager, IZeroStore store) + { + Context = context; + SignInManager = signInManager; + Store = store; + } + + + /// + public bool IsLoggedIn() + { + return SignInManager.IsSignedIn(Context.BackofficeUser); + } + + + /// + public bool IsSuper() + { + return Context.BackofficeUser.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); + } + + + /// + public bool IsAdmin() + { + return Context.BackofficeUser.HasClaim(Constants.Auth.Claims.Role, "administrator"); // TODO use constant (in setup as well) + } + + + /// + public async Task GetUser() + { + return await SignInManager.UserManager.GetUserAsync(Context.BackofficeUser); + } + + + /// + public IList GetPermissions(string prefix = null) + { + return new List(); // TODO + //return Context.BackofficeUser.Claims + // .Where(claim => claim.Type == Constants.Auth.Claims.Permission && (prefix == null || claim.Value.StartsWith(prefix))) + // .Select(claim => Permission.FromClaim(claim, prefix)) + // .ToList(); + } + + + /// + public Permission GetPermission(string key = null) + { + return new Permission(); + //Claim claim = Context.BackofficeUser.Claims.FirstOrDefault(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(key + ":")); + //return Permission.FromClaim(claim); + } + + + /// + public async Task Login(string email, string password, bool isPersistent) + { + EntityResult result = new EntityResult(); + + ZeroUser user = await SignInManager.UserManager.FindByNameAsync(email); + + if (user == null) + { + result.AddError("@login.errors.wrongcredentials"); // TODO we don't need translations here, but return an enum, so the app itself can translate the error + return result; + } + // TODO probably move this logic into a custom SignInManager which overrides CanSignInAsync() + // see https://stackoverflow.com/a/35484758/670860 + else if (!user.IsActive) + { + result.AddError("@login.errors.disabled"); + return result; + } + + SignInResult signInResult = await SignInManager.PasswordSignInAsync(user, password, isPersistent, true); + + if (!signInResult.Succeeded) + { + if (signInResult.IsLockedOut) + { + result.AddError("@login.errors.lockedout"); + } + else if (signInResult.IsNotAllowed) + { + result.AddError("@login.errors.notallowed"); + } + else if (signInResult.RequiresTwoFactor) + { + result.AddError("@login.errors.requirestwofactor"); + } + else + { + result.AddError("@login.errors.wrongcredentials"); + } + + return result; + } + + return EntityResult.Success(); + } + + + /// + public async Task Logout() + { + await SignInManager.SignOutAsync(); + } + + + /// + public string GetUserId() + { + return SignInManager.UserManager.GetUserId(Context.BackofficeUser); + } + + + /// + public async Task TrySwitchApp(string appId) + { + IZeroDocumentSession session = Store.Session(global: true); + ZeroUser user = await GetUser(); + + if (user == null || appId.IsNullOrEmpty()) + { + return false; + } + + string[] allowedAppIds = user.GetAllowedAppIds(); + + 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 + + await session.StoreAsync(user); + await session.SaveChangesAsync(); + + return true; + } + + return false; + } +} + + +public interface IAuthenticationService +{ + /// + /// Get currently logged-in user + /// + Task GetUser(); + + /// + /// Whether a user is currently logged-in + /// + bool IsLoggedIn(); + + /// + /// Whether the current user is the super user who created the zero instance + /// + bool IsSuper(); + + /// + /// Whether the current user belongs to the administrator role (will always return false if this role gets deleted) + /// + bool IsAdmin(); + + /// + /// Logs a zero-user in and sets cookie + /// + Task Login(string email, string password, bool isPersistent); + + /// + /// Logs out the current user + /// + Task Logout(); + + /// + /// Get the ID of the currently logged in user + /// + string GetUserId(); + + /// + /// Get all permissions for the current user with an optional prefix + /// + IList GetPermissions(string prefix = null); + + /// + /// Get a single permissions by key + /// + Permission GetPermission(string key = null); + + /// + /// Tries to switch the currently loaded backoffice application for the current user + /// + Task TrySwitchApp(string appId); +} \ No newline at end of file diff --git a/zero.Core/Identity/Services/AuthorizationService.cs b/zero.Core/Identity/Services/AuthorizationService.cs new file mode 100644 index 00000000..09f9f899 --- /dev/null +++ b/zero.Core/Identity/Services/AuthorizationService.cs @@ -0,0 +1,135 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using System.Security.Claims; + +namespace zero.Identity; + +public class AuthorizationService : IAuthorizationService +{ + protected IHttpContextAccessor HttpContextAccessor { get; set; } + + protected SignInManager SignInManager { get; private set; } + + protected ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; + + + public AuthorizationService(IHttpContextAccessor httpContextAccessor, SignInManager signInManager) + { + HttpContextAccessor = httpContextAccessor; + SignInManager = signInManager; + } + + + /// + public bool IsLoggedIn() + { + ClaimsPrincipal principal = HttpContextAccessor.HttpContext.User; + + bool isAuthenticated = principal.Identity.IsAuthenticated; + bool isZeroUser = principal.HasClaim(Constants.Auth.Claims.IsZero, PermissionsValue.True); + + bool isSignedIn = SignInManager.IsSignedIn(principal); + + return isAuthenticated && isZeroUser && isSignedIn; + } + + + /// + public bool IsSuper() + { + return Principal.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); + } + + + /// + public bool IsAdmin() + { + return Principal.HasClaim(Constants.Auth.Claims.Role, "administrator"); // TODO use constant (in setup as well) + } + + + /// + public IList GetPermissions(string prefix = null) + { + return new List(); // TODO + //return Principal.Claims + // .Where(claim => claim.Type == Constants.Auth.Claims.Permission && (prefix == null || claim.Value.StartsWith(prefix))) + // .Select(claim => Permission.FromClaim(claim, prefix)) + // .ToList(); + } + + + /// + public Permission GetPermission(string key = null) + { + return new Permission(); + //Claim claim = Principal.Claims.FirstOrDefault(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(key + ":")); + + //return Permission.FromClaim(claim); + } + + + public EntityPermission GetPermissionForEntity(T model, string permissionKey) where T : ZeroEntity + { + EntityPermission result = new EntityPermission(); + + if (!IsLoggedIn()) + { + return result; + } + + Type type = typeof(T); + bool isSuperUser = Principal.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); + + //result.IsAppAware = AppAwareType.IsAssignableFrom(type); // TODO appx + //result.IsShareable = result.IsAppAware && AppAwareShareableType.IsAssignableFrom(type); + + if (isSuperUser) + { + result.CanCreate = true; + result.CanCreateShared = result.CanCreate && result.IsShareable; + result.CanEdit = true; + result.CanRead = true; + result.CanDelete = true; + return result; + } + + Permission permission = GetPermission(permissionKey); + + if (permission != null) + { + + } + + return result; + } +} + + +public interface IAuthorizationService +{ + /// + /// Whether a user is currently logged-in + /// + bool IsLoggedIn(); + + /// + /// Whether the current user is the super user who created the zero instance + /// + bool IsSuper(); + + /// + /// Whether the current user belongs to the administrator role (will always return false if this role gets deleted) + /// + bool IsAdmin(); + + /// + /// Get all permissions for the current user with an optional prefix + /// + IList GetPermissions(string prefix = null); + + /// + /// Get a single permissions by key + /// + public Permission GetPermission(string key = null); +} \ No newline at end of file diff --git a/zero.Core/Identity/Services/UserRolesService.cs b/zero.Core/Identity/Services/UserRolesService.cs new file mode 100644 index 00000000..0282f2f3 --- /dev/null +++ b/zero.Core/Identity/Services/UserRolesService.cs @@ -0,0 +1,114 @@ +using FluentValidation.Results; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Identity; +using Raven.Client.Documents; +using System.Security.Claims; + +namespace zero.Identity; + +public class UserRolesService : BackofficeApi, IUserRolesService +{ + protected IHttpContextAccessor HttpContextAccessor { get; set; } + + protected UserManager UserManager { get; private set; } + + protected RoleManager RoleManager { get; private set; } + + private ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; + + + public UserRolesService(IHttpContextAccessor httpContextAccessor, UserManager userManager, RoleManager roleManager, IStoreContext store) : base(store, isCoreDatabase: true) + { + HttpContextAccessor = httpContextAccessor; + UserManager = userManager; + RoleManager = roleManager; + } + + + /// + public async Task> GetAll() + { + return await Session.Query().OrderBy(x => x.Sort).ThenBy(x => x.Name).ToListAsync(); + } + + + /// + public async Task GetById(string id) + { + return await RoleManager.FindByIdAsync(id); + } + + + /// + public async Task> Save(ZeroUserRole model) + { + ValidationResult validation = await new UserRoleValidator().ValidateAsync(model); + + if (!validation.IsValid) + { + return EntityResult.Fail(validation); + } + + if (model.Id.IsNullOrEmpty()) + { + model.CreatedDate = DateTimeOffset.Now; + } + + model.Alias = Safenames.Alias(model.Name); + + await Session.StoreAsync(model); + + string id = Session.Advanced.GetDocumentId(model); + + await Session.SaveChangesAsync(); + + if (model.Id.IsNullOrEmpty()) + { + model.Id = id; + } + + return EntityResult.Success(model); + } + + + /// + public async Task> Delete(string id) + { + ZeroUserRole country = await Session.LoadAsync(id); + + if (country == null) + { + return EntityResult.Fail("@errors.ondelete.idnotfound"); + } + + Session.Delete(country); + + await Session.SaveChangesAsync(); + + return EntityResult.Success(); + } +} + + +public interface IUserRolesService : IBackofficeApi +{ + /// + /// Get all user roles + /// + Task> GetAll(); + + /// + /// Get role by id + /// + Task GetById(string id); + + /// + /// Create or update a role + /// + Task> Save(ZeroUserRole model); + + /// + /// Deletes a role + /// + Task> Delete(string id); +} \ No newline at end of file diff --git a/zero.Core/Identity/Services/UserService.cs b/zero.Core/Identity/Services/UserService.cs new file mode 100644 index 00000000..dc8df408 --- /dev/null +++ b/zero.Core/Identity/Services/UserService.cs @@ -0,0 +1,275 @@ +using Microsoft.AspNetCore.Identity; +using Raven.Client.Documents; +using Raven.Client.Documents.Linq; + +namespace zero.Identity; + +public class UserService : BackofficeApi, IUserService +{ + protected UserManager UserManager { get; private set; } + + public UserService(IStoreContext store, UserManager userManager) : base(store, isCoreDatabase: true) + { + UserManager = userManager; + } + + + /// + public async Task GetUserById(string id) + { + ZeroUser user = await UserManager.FindByIdAsync(id); + return user; + } + + + /// + public async Task GetUserByEmail(string email) + { + ZeroUser user = await UserManager.FindByEmailAsync(email); + return user; + } + + + /// + public async Task> GetByIds(params string[] ids) + { + return await GetByIds(ids); + } + + + /// + public async Task> GetAll() + { + return await Session.Query() + .OrderByDescending(x => x.CreatedDate) + .ToListAsync(); + } + + + /// + public async Task> GetByQuery(ListQuery query) + { + string currentUserId = UserManager.GetUserId(Context.Context.BackofficeUser); + + query.SearchSelector = user => user.Name; + + return await Session.Query() + .ToQueriedListAsync(query); + } + + + /// + public async Task> Save(ZeroUser model) + { + bool updateSecurityStamp = false; + + if (!model.Id.IsNullOrEmpty()) + { + ZeroUser origin = await GetUserById(model.Id); + updateSecurityStamp = origin != null && model.PasswordHash != origin.PasswordHash; + } + + EntityResult result = await SaveModel(model); //, new UserValidator()); + + if (updateSecurityStamp) + { + await UserManager.UpdateSecurityStampAsync(model); + } + + return result; + } + + + /// + public async Task> Delete(string id) + { + return await DeleteById(id); + } + + + /// + public async Task> HashPassword(ZeroUser user, string currentPassword, string newPassword, string confirmNewPassword) + { + if (newPassword != confirmNewPassword) + { + return EntityResult.Fail(nameof(newPassword), "@errors.changepassword.newpasswordsnotmatching"); + } + + if (currentPassword.IsNullOrWhiteSpace() || newPassword.IsNullOrWhiteSpace()) + { + return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); + } + + if (user == null) + { + return EntityResult.Fail("@errors.changepassword.nouser"); + } + + if (UserManager.PasswordHasher.VerifyHashedPassword(user, user.PasswordHash, currentPassword) != PasswordVerificationResult.Success) + { + return EntityResult.Fail("@errors.changepassword.passwordincorrect"); + } + + // validate new password + List errors = new(); + bool isValid = true; + foreach (var v in UserManager.PasswordValidators) + { + var result = await v.ValidateAsync(UserManager, user, newPassword); + if (!result.Succeeded) + { + if (result.Errors.Any()) + { + errors.AddRange(result.Errors); + } + + isValid = false; + } + } + + if (!isValid) + { + EntityResult result = EntityResult.Fail(); + foreach (IdentityError error in errors) + { + result.AddError(error.Description); + } + return result; + } + + return EntityResult.Success(UserManager.PasswordHasher.HashPassword(user, newPassword)); + } + + + /// + public async Task> UpdatePassword(ZeroUser user, string currentPassword, string newPassword) + { + if (currentPassword.IsNullOrWhiteSpace() || newPassword.IsNullOrWhiteSpace()) + { + return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); + } + + if (user == null) + { + return EntityResult.Fail("@errors.changepassword.nouser"); + } + + IdentityResult identityResult = await UserManager.ChangePasswordAsync(user as ZeroUser, currentPassword, newPassword); + + if (!identityResult.Succeeded) + { + EntityResult result = EntityResult.Fail(); + + foreach (IdentityError error in identityResult.Errors) + { + result.AddError(error.Description); + } + + return result; + } + + return EntityResult.Success(user); + } + + + /// + public async Task> Enable(ZeroUser user) + { + return await UpdateActiveState(user, true); + } + + + /// + public async Task> Disable(ZeroUser user) + { + return await UpdateActiveState(user, false); + } + + + /// + /// Updates the active state of user. + /// If IsActive=false, the user cannot login anymore + /// + async Task> UpdateActiveState(ZeroUser user, bool isActive) + { + user.IsActive = isActive; + + IdentityResult identityResult = await UserManager.UpdateAsync(user as ZeroUser); + + if (!identityResult.Succeeded) + { + EntityResult result = EntityResult.Fail(); + + foreach (IdentityError error in identityResult.Errors) + { + result.AddError(error.Description); + } + + return result; + } + + await UserManager.UpdateSecurityStampAsync(user as ZeroUser); + + return EntityResult.Success(user); + } +} + + +public interface IUserService : IBackofficeApi +{ + /// + /// Find user by id + /// + Task GetUserById(string id); + + /// + /// Find user by email + /// + Task GetUserByEmail(string email); + + /// + /// Get users by ids + /// + Task> GetByIds(params string[] ids); + + /// + /// Get all users for the selected application + /// + Task> GetAll(); + + /// + /// Get all available users (with query) + /// + Task> GetByQuery(ListQuery query); + + /// + /// Creates or updates a user + /// + Task> Save(ZeroUser model); + + /// + /// Deletes a user + /// + Task> Delete(string id); + + /// + /// Changes the password of the current user. + /// User is logged out if this operation succeeds. + /// + Task> UpdatePassword(ZeroUser user, string currentPassword, string newPassword); + + /// + /// Tries to hash a new password + /// + Task> HashPassword(ZeroUser user, string currentPassword, string newPassword, string confirmNewPassword); + + /// + /// Enables a user + /// + Task> Enable(ZeroUser user); + + /// + /// Disables a user + /// + Task> Disable(ZeroUser user); +} \ No newline at end of file diff --git a/zero.Core/Identity/UserIdentity.cs b/zero.Core/Identity/UserIdentity.cs index 5cbe7adc..599c963b 100644 --- a/zero.Core/Identity/UserIdentity.cs +++ b/zero.Core/Identity/UserIdentity.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Security.Claims; +using System.Security.Claims; using System.Security.Principal; namespace zero.Identity; diff --git a/zero.Core/Identity/Validation/BackofficeUserRoleValidator.cs b/zero.Core/Identity/Validation/BackofficeUserRoleValidator.cs index aff1031e..86bf56b5 100644 --- a/zero.Core/Identity/Validation/BackofficeUserRoleValidator.cs +++ b/zero.Core/Identity/Validation/BackofficeUserRoleValidator.cs @@ -1,9 +1,8 @@ using FluentValidation; -using System; namespace zero.Identity; -public class BackofficeUserRoleValidator : ZeroValidator +public class BackofficeUserRoleValidator : ZeroValidator { const string SECTION_CLAIM = "section."; diff --git a/zero.Core/Identity/Validation/BackofficeUserValidator.cs b/zero.Core/Identity/Validation/BackofficeUserValidator.cs index 5ee45f36..424bd32a 100644 --- a/zero.Core/Identity/Validation/BackofficeUserValidator.cs +++ b/zero.Core/Identity/Validation/BackofficeUserValidator.cs @@ -2,7 +2,7 @@ namespace zero.Identity; -public class BackofficeUserValidator : ZeroValidator +public class BackofficeUserValidator : ZeroValidator { public BackofficeUserValidator(bool isCreate = false) { diff --git a/zero.Core/Localization/Module.cs b/zero.Core/Localization/Module.cs deleted file mode 100644 index ecf61e1c..00000000 --- a/zero.Core/Localization/Module.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Localization; - -internal class LocalizationModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/zero.Core/Localization/ServiceCollectionExtensions.cs b/zero.Core/Localization/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..260aac34 --- /dev/null +++ b/zero.Core/Localization/ServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Localization; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroLocalization(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Mails/MailProvider.cs b/zero.Core/Mails/MailProvider.cs index 8fcfd977..675e72dc 100644 --- a/zero.Core/Mails/MailProvider.cs +++ b/zero.Core/Mails/MailProvider.cs @@ -7,7 +7,7 @@ namespace zero.Mails; public class MailProvider : IMailProvider { - protected IMailTemplatesCollection Collection { get; set; } + protected IMailTemplatesStore Collection { get; set; } protected ILogger Logger { get; set; } @@ -22,7 +22,7 @@ public class MailProvider : IMailProvider private Encoding encoding = Encoding.UTF8; - public MailProvider(IZeroContext zero, IMailTemplatesCollection collection, ILogger logger, IMailDispatcher mailSender, IRazorRenderer renderer) + public MailProvider(IZeroContext zero, IMailTemplatesStore collection, ILogger logger, IMailDispatcher mailSender, IRazorRenderer renderer) { Zero = zero; Collection = collection; diff --git a/zero.Core/Mails/Module.cs b/zero.Core/Mails/Module.cs deleted file mode 100644 index a2c1d398..00000000 --- a/zero.Core/Mails/Module.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Mails; - -internal class MailsModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/zero.Core/Mails/ServiceCollectionExtensions.cs b/zero.Core/Mails/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..6165788b --- /dev/null +++ b/zero.Core/Mails/ServiceCollectionExtensions.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Mails; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroMails(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Pages/PagesModule.cs b/zero.Core/Pages/PagesModule.cs deleted file mode 100644 index 8ea5dfd6..00000000 --- a/zero.Core/Pages/PagesModule.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace zero.Pages; - -internal class PagesModule : ZeroModule -{ - /// - public override void Configure(IZeroOptions options) - { - options.Raven.Indexes.Add(); - options.Raven.Indexes.Add(); - options.Raven.Indexes.Add(); - options.Raven.Indexes.Add(); - } -} \ No newline at end of file diff --git a/zero.Core/Pages/ServiceCollectionExtensions.cs b/zero.Core/Pages/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..5fc3c3ec --- /dev/null +++ b/zero.Core/Pages/ServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Pages; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroPages(this IServiceCollection services) + { + services.Configure(opts => + { + RavenOptions raven = opts.For(); + raven.Indexes.Add(); + raven.Indexes.Add(); + raven.Indexes.Add(); + raven.Indexes.Add(); + }); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Persistence/Module.cs b/zero.Core/Persistence/ServiceCollectionExtensions.cs similarity index 57% rename from zero.Core/Persistence/Module.cs rename to zero.Core/Persistence/ServiceCollectionExtensions.cs index aa4e00a7..c21f0696 100644 --- a/zero.Core/Persistence/Module.cs +++ b/zero.Core/Persistence/ServiceCollectionExtensions.cs @@ -4,31 +4,22 @@ using Raven.Client.Http; namespace zero.Persistence; -internal class PersistenceModule : ZeroModule +internal static class ServiceCollectionExtensions { - /// - public override void Register(IZeroModuleConfiguration config) + public static IServiceCollection AddZeroPersistence(this IServiceCollection services) { - config.Services.AddSingleton(); - config.Services.AddSingleton(CreateRavenStore); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - } - - - /// - public override void Configure(IZeroOptions options) - { - base.Configure(options); + services.AddSingleton(); + services.AddSingleton(CreateRavenStore); + services.AddScoped(); + services.AddScoped(); + return services; } /// /// Creates and configures the raven store /// - protected ZeroDocumentStore CreateRavenStore(IServiceProvider services) + static ZeroDocumentStore CreateRavenStore(IServiceProvider services) { IZeroOptions options = services.GetService(); IZeroDocumentConventionsBuilder conventionsBuilder = services.GetService(); diff --git a/zero.Core/Registrations.cs b/zero.Core/Registrations.cs deleted file mode 100644 index 3d64c28b..00000000 --- a/zero.Core/Registrations.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace zero; - -internal class Registrations -{ - public static List Modules { get; } = new() - { - new ArchitectureModule(), - new ApplicationsModule(), - new ContextModule(), - new CommunicationModule(), - new ConfigurationModule(), - new IdentityModule(), - new LocalizationModule(), - new MailsModule(), - new PagesModule(), - new PersistenceModule(), - new RenderingModule(), - new RoutingModule() - }; - - - public static List Plugins { get; } = new() - { - - }; -} \ No newline at end of file diff --git a/zero.Core/Rendering/Module.cs b/zero.Core/Rendering/Module.cs deleted file mode 100644 index e485addd..00000000 --- a/zero.Core/Rendering/Module.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; - -namespace zero.Rendering; - -internal class RenderingModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - } -} \ No newline at end of file diff --git a/zero.Core/Rendering/ServiceCollectionExtensions.cs b/zero.Core/Rendering/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..114b5b90 --- /dev/null +++ b/zero.Core/Rendering/ServiceCollectionExtensions.cs @@ -0,0 +1,12 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Rendering; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroRendering(this IServiceCollection services) + { + services.AddScoped(); + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Routing/Module.cs b/zero.Core/Routing/Module.cs deleted file mode 100644 index c6edb493..00000000 --- a/zero.Core/Routing/Module.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace zero.Routing; - -internal class RoutingModule : ZeroModule -{ - /// - public override void Register(IZeroModuleConfiguration config) - { - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.AddScoped(); - config.Services.TryAddEnumerable(ServiceDescriptor.Singleton()); - config.Services.AddScoped(); - } - - - /// - public override void Configure(IZeroOptions options) - { - RavenOptions raven = options.For(); - InterceptorOptions interceptors = options.For(); - - raven.Indexes.Add(); - raven.Indexes.Add(); - raven.Indexes.Add(); - } -} \ No newline at end of file diff --git a/zero.Core/Routing/ServiceCollectionExtensions.cs b/zero.Core/Routing/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..5fc53bf7 --- /dev/null +++ b/zero.Core/Routing/ServiceCollectionExtensions.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Routing; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace zero.Routing; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroRouting(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); + services.AddScoped(); + + services.Configure(opts => + { + RavenOptions raven = opts.For(); + raven.Indexes.Add(); + raven.Indexes.Add(); + raven.Indexes.Add(); + }); + + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Routing/ZeroEnpointRouteBuilderExtensions.cs b/zero.Core/Routing/ZeroEnpointRouteBuilderExtensions.cs index 8d24c4ff..a194d21c 100644 --- a/zero.Core/Routing/ZeroEnpointRouteBuilderExtensions.cs +++ b/zero.Core/Routing/ZeroEnpointRouteBuilderExtensions.cs @@ -10,7 +10,7 @@ public static class ZeroEndpointRouteBuilderExtensions // see https://our.umbraco.com/documentation/reference/routing/custom-routes#where-to-put-your-routing-logic //string path = options.BackofficePath.EnsureStartsWith('/').TrimEnd('/'); - endpoints.MapFallbackToController(path + "/{**path}", "Index", "ZeroBackoffice"); + endpoints.MapFallbackToController(path + "/{**path}", "Index", "ZeroIndex"); return endpoints; } diff --git a/zero.Core/Collections/CachableEntityCollection.cs b/zero.Core/Stores/CachableEntityStore.cs similarity index 55% rename from zero.Core/Collections/CachableEntityCollection.cs rename to zero.Core/Stores/CachableEntityStore.cs index 74f00932..7e1d3c50 100644 --- a/zero.Core/Collections/CachableEntityCollection.cs +++ b/zero.Core/Stores/CachableEntityStore.cs @@ -1,16 +1,16 @@ -namespace zero.Collections; +namespace zero.Stores; -public record CachableEntityCollectionOptions(bool CacheIndividual, bool CacheAll); +public record CachableEntityStoreOptions(bool CacheIndividual, bool CacheAll); -public abstract class CachableEntityCollection : EntityCollection where T : ZeroIdEntity, new() +public abstract class CachableEntityStore : EntityStore where T : ZeroIdEntity, new() { - protected CachableEntityCollectionOptions CacheOptions { get; set; } + protected CachableEntityStoreOptions CacheOptions { get; set; } - protected ICollectionCache Cache { get; set; } + protected IStoreCache Cache { get; set; } - public CachableEntityCollection(ICollectionContext collectionContext, ICollectionCache collectionCache) : base(collectionContext) + public CachableEntityStore(IStoreContext collectionContext, IStoreCache collectionCache) : base(collectionContext) { CacheOptions = new(CacheIndividual: true, CacheAll: false); // TODO when props update we need to update Cache.For() Cache = collectionCache.For(CacheOptions); diff --git a/zero.Core/Stores/CountriesStore.cs b/zero.Core/Stores/CountriesStore.cs new file mode 100644 index 00000000..a17c37e6 --- /dev/null +++ b/zero.Core/Stores/CountriesStore.cs @@ -0,0 +1,18 @@ +using FluentValidation; + +namespace zero.Stores; + +public class CountriesStore : CachableEntityStore, ICountriesStore +{ + public CountriesStore(IStoreContext context, IStoreCache cache) : base(context, cache) { } + + /// + protected override void ValidationRules(ZeroValidator validator) + { + validator.RuleFor(x => x.Code).Length(2).Unique(Session); + validator.RuleFor(x => x.Name).Length(2, 120); + } +} + + +public interface ICountriesStore : IEntityStore { } \ No newline at end of file diff --git a/zero.Core/Collections/EntityCollection.cs b/zero.Core/Stores/EntityStore.cs similarity index 94% rename from zero.Core/Collections/EntityCollection.cs rename to zero.Core/Stores/EntityStore.cs index 9b1f1f4b..5af99033 100644 --- a/zero.Core/Collections/EntityCollection.cs +++ b/zero.Core/Stores/EntityStore.cs @@ -2,9 +2,9 @@ using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; -namespace zero.Collections; +namespace zero.Stores; -public abstract class EntityCollection : IEntityCollection where T : ZeroIdEntity, new() +public abstract class EntityStore : IEntityStore where T : ZeroIdEntity, new() { /// public Guid Guid { get; private set; } = Guid.NewGuid(); @@ -21,10 +21,10 @@ public abstract class EntityCollection : IEntityCollection where T : ZeroI protected IInterceptors Interceptors { get; private set; } - protected ICollectionOperations Operations { get; private set; } + protected IStoreOperations Operations { get; private set; } - public EntityCollection(ICollectionContext collectionContext) + public EntityStore(IStoreContext collectionContext) { Operations = collectionContext.Operations; Context = collectionContext.Context; @@ -98,7 +98,7 @@ public abstract class EntityCollection : IEntityCollection where T : ZeroI -public interface IEntityCollection where T : ZeroIdEntity, new() +public interface IEntityStore where T : ZeroIdEntity, new() { /// /// Get new instance of an entity diff --git a/zero.Core/Collections/LanguagesCollection.cs b/zero.Core/Stores/LanguagesStore.cs similarity index 87% rename from zero.Core/Collections/LanguagesCollection.cs rename to zero.Core/Stores/LanguagesStore.cs index 8cc5dc73..670c762e 100644 --- a/zero.Core/Collections/LanguagesCollection.cs +++ b/zero.Core/Stores/LanguagesStore.cs @@ -3,11 +3,11 @@ using Raven.Client.Documents; using Raven.Client.Documents.Linq; using System.Globalization; -namespace zero.Collections; +namespace zero.Stores; -public class LanguagesCollection : EntityCollection, ILanguagesCollection +public class LanguagesStore : EntityStore, ILanguagesStore { - public LanguagesCollection(ICollectionContext context) : base(context) { } + public LanguagesStore(IStoreContext context) : base(context) { } /// @@ -42,7 +42,7 @@ public class LanguagesCollection : EntityCollection, ILanguagesCollect } -public interface ILanguagesCollection : IEntityCollection +public interface ILanguagesStore : IEntityStore { /// /// Get all available cultures diff --git a/zero.Core/Collections/MailTemplatesCollection.cs b/zero.Core/Stores/MailTemplatesStore.cs similarity index 73% rename from zero.Core/Collections/MailTemplatesCollection.cs rename to zero.Core/Stores/MailTemplatesStore.cs index 5cc5c0a3..3ad6ef72 100644 --- a/zero.Core/Collections/MailTemplatesCollection.cs +++ b/zero.Core/Stores/MailTemplatesStore.cs @@ -1,14 +1,14 @@ using FluentValidation; using Raven.Client.Documents; -namespace zero.Collections; +namespace zero.Stores; -public class MailTemplatesCollection : EntityCollection, IMailTemplatesCollection +public class MailTemplatesStore : EntityStore, IMailTemplatesStore { protected IMailDispatcher Dispatcher { get; set; } - public MailTemplatesCollection(ICollectionContext context, IMailDispatcher dispatcher = null) : base(context) + public MailTemplatesStore(IStoreContext context, IMailDispatcher dispatcher = null) : base(context) { Dispatcher = dispatcher; } @@ -37,7 +37,7 @@ public class MailTemplatesCollection : EntityCollection, IMailTemp } -public interface IMailTemplatesCollection : IEntityCollection +public interface IMailTemplatesStore : IEntityStore { /// /// Get mail template by associated key diff --git a/zero.Core/Stores/ServiceCollectionExtensions.cs b/zero.Core/Stores/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..dbbafcbc --- /dev/null +++ b/zero.Core/Stores/ServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace zero.Stores; + +internal static class ServiceCollectionExtensions +{ + public static IServiceCollection AddZeroStores(this IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddSingleton(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + return services; + } +} \ No newline at end of file diff --git a/zero.Core/Collections/CollectionCache.cs b/zero.Core/Stores/StoreCache.cs similarity index 60% rename from zero.Core/Collections/CollectionCache.cs rename to zero.Core/Stores/StoreCache.cs index 8549e144..8c17307b 100644 --- a/zero.Core/Collections/CollectionCache.cs +++ b/zero.Core/Stores/StoreCache.cs @@ -1,13 +1,13 @@ using System.Collections.Concurrent; -namespace zero.Collections; +namespace zero.Stores; -public class CollectionCache : ICollectionCache +public class StoreCache : IStoreCache { protected ConcurrentDictionary _cache = new(); - public CollectionCache() + public StoreCache() { } @@ -24,16 +24,16 @@ public class CollectionCache : ICollectionCache return false; } - public ICollectionCache For(CachableEntityCollectionOptions options) + public IStoreCache For(CachableEntityStoreOptions options) { return this; } } -public interface ICollectionCache +public interface IStoreCache { - ICollectionCache For(CachableEntityCollectionOptions options); + IStoreCache For(CachableEntityStoreOptions options); bool TryGetValue(string id, out T model); } \ No newline at end of file diff --git a/zero.Core/Collections/CollectionContext.cs b/zero.Core/Stores/StoreContext.cs similarity index 60% rename from zero.Core/Collections/CollectionContext.cs rename to zero.Core/Stores/StoreContext.cs index 0e05180c..9b1bd523 100644 --- a/zero.Core/Collections/CollectionContext.cs +++ b/zero.Core/Stores/StoreContext.cs @@ -1,6 +1,6 @@ -namespace zero.Collections; +namespace zero.Stores; -public class CollectionContext : ICollectionContext +public class StoreContext : IStoreContext { public IZeroStore Store { get; private set; } @@ -10,10 +10,10 @@ public class CollectionContext : ICollectionContext public IInterceptors Interceptors { get; private set; } - public ICollectionOperations Operations { get; private set; } + public IStoreOperations Operations { get; private set; } - public CollectionContext(IZeroContext context, IInterceptors interceptors, ICollectionOperations operations) + public StoreContext(IZeroContext context, IInterceptors interceptors, IStoreOperations operations) { Store = context.Store; Options = context.Options; @@ -24,7 +24,7 @@ public class CollectionContext : ICollectionContext } -public interface ICollectionContext +public interface IStoreContext { IZeroStore Store { get; } @@ -34,5 +34,5 @@ public interface ICollectionContext IInterceptors Interceptors { get; } - ICollectionOperations Operations { get; } + IStoreOperations Operations { get; } } \ No newline at end of file diff --git a/zero.Core/Collections/CollectionOperations.Delete.cs b/zero.Core/Stores/StoreOperations.Delete.cs similarity index 94% rename from zero.Core/Collections/CollectionOperations.Delete.cs rename to zero.Core/Stores/StoreOperations.Delete.cs index 5d1df1f0..281e365a 100644 --- a/zero.Core/Collections/CollectionOperations.Delete.cs +++ b/zero.Core/Stores/StoreOperations.Delete.cs @@ -1,6 +1,6 @@ -namespace zero.Collections; +namespace zero.Stores; -public abstract partial class CollectionOperations +public abstract partial class StoreOperations { /// public virtual async Task> Delete(string id) where T : ZeroIdEntity, new() diff --git a/zero.Core/Collections/CollectionOperations.Read.cs b/zero.Core/Stores/StoreOperations.Read.cs similarity index 97% rename from zero.Core/Collections/CollectionOperations.Read.cs rename to zero.Core/Stores/StoreOperations.Read.cs index db2e5550..5ad628cd 100644 --- a/zero.Core/Collections/CollectionOperations.Read.cs +++ b/zero.Core/Stores/StoreOperations.Read.cs @@ -3,9 +3,9 @@ using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; using Raven.Client.Documents.Session; -namespace zero.Collections; +namespace zero.Stores; -public abstract partial class CollectionOperations +public abstract partial class StoreOperations { /// public virtual async Task Load(string id, string changeVector = null) where T : ZeroIdEntity, new() diff --git a/zero.Core/Collections/CollectionOperations.Write.cs b/zero.Core/Stores/StoreOperations.Write.cs similarity index 95% rename from zero.Core/Collections/CollectionOperations.Write.cs rename to zero.Core/Stores/StoreOperations.Write.cs index a116b616..65eb704c 100644 --- a/zero.Core/Collections/CollectionOperations.Write.cs +++ b/zero.Core/Stores/StoreOperations.Write.cs @@ -1,8 +1,8 @@ using FluentValidation.Results; -namespace zero.Collections; +namespace zero.Stores; -public abstract partial class CollectionOperations +public abstract partial class StoreOperations { /// public virtual Task> Create(T model, Func> validate = null) where T : ZeroIdEntity, new() => Save(model, validate); diff --git a/zero.Core/Collections/CollectionOperations.cs b/zero.Core/Stores/StoreOperations.cs similarity index 96% rename from zero.Core/Collections/CollectionOperations.cs rename to zero.Core/Stores/StoreOperations.cs index 90973696..7f84d689 100644 --- a/zero.Core/Collections/CollectionOperations.cs +++ b/zero.Core/Stores/StoreOperations.cs @@ -3,9 +3,9 @@ using Raven.Client.Documents.Indexes; using Raven.Client.Documents.Linq; using System.Security.Claims; -namespace zero.Collections; +namespace zero.Stores; -public abstract partial class CollectionOperations : ICollectionOperations +public abstract partial class StoreOperations : IStoreOperations { /// public IZeroDocumentSession Session => Context.Store.Session(); @@ -19,7 +19,7 @@ public abstract partial class CollectionOperations : ICollectionOperations protected IInterceptors Interceptors { get; private set; } - public CollectionOperations(ICollectionContext collectionContext) + public StoreOperations(IStoreContext collectionContext) { Context = collectionContext.Context; Interceptors = collectionContext.Interceptors; @@ -115,7 +115,7 @@ public abstract partial class CollectionOperations : ICollectionOperations -public interface ICollectionOperations +public interface IStoreOperations { /// /// Get new instance of an entity diff --git a/zero.Core/Usings.cs b/zero.Core/Usings.cs index c6934da0..8333a8d5 100644 --- a/zero.Core/Usings.cs +++ b/zero.Core/Usings.cs @@ -22,5 +22,5 @@ global using zero.Rendering; global using zero.Routing; global using zero.Utils; global using zero.Validation; -global using zero.Collections; +global using zero.Stores; global using zero.Models; \ No newline at end of file diff --git a/zero.Core/ZeroApplicationBuilder.cs b/zero.Core/ZeroApplicationBuilder.cs index 7069bcf6..db7c5077 100644 --- a/zero.Core/ZeroApplicationBuilder.cs +++ b/zero.Core/ZeroApplicationBuilder.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; namespace zero; @@ -17,6 +18,8 @@ public class ZeroApplicationBuilder : IZeroApplicationBuilder App.UseRouting(); App.UseAuthentication(); App.UseAuthorization(); + + ZeroModuleInitializer.ConfigureAll(app.ApplicationServices.GetRequiredService()); } diff --git a/zero.Core/ZeroBuilder.cs b/zero.Core/ZeroBuilder.cs index b800ff5b..f1296a32 100644 --- a/zero.Core/ZeroBuilder.cs +++ b/zero.Core/ZeroBuilder.cs @@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.IO; namespace zero; @@ -21,8 +20,6 @@ public class ZeroBuilder public ZeroBuilder(IServiceCollection services, IConfiguration configuration, Action setupAction) { - ZeroModuleInitializer.RegisterAll(new ZeroModuleConfiguration(services, configuration)); - Services = services; Mvc = services.AddMvc(); Configuration = configuration; @@ -37,44 +34,34 @@ public class ZeroBuilder new AssemblyDiscovery(Mvc).Execute(StartupOptions.AssemblyDiscoveryRules); - // add default plugin - AddPlugin(); + services.AddZeroApplications(); + services.AddZeroBlueprints(); + services.AddZeroCommunication(); + services.AddZeroConfiguration(Configuration); + services.AddZeroContext(); + services.AddZeroFileStorage(); + services.AddZeroIdentity(); + services.AddZeroLocalization(); + services.AddZeroMails(); + services.AddZeroPages(); + services.AddZeroPersistence(); + services.AddZeroRendering(); + services.AddZeroRouting(); + services.AddZeroStores(); + //Mvc.AddNewtonsoftJson(x => + //{ + // // TODO this shall only be configurated for backoffice controllers + // BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings); + //}); - Mvc.AddNewtonsoftJson(x => - { - // TODO this shall only be configurated for backoffice controllers - BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings); - }); - - if (Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1") - { - Mvc.AddRazorRuntimeCompilation(); - } + //if (Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1") + //{ + // Mvc.AddRazorRuntimeCompilation(); + //} // configure FluentValidation ValidatorOptions.Global.PropertyNameResolver = ValidatorCamelCasePropertyResolver.ResolvePropertyName; - - - // add default services - Services.AddScoped(); - Services.AddScoped(); - Services.AddScoped(); - - //Services.AddScoped(); - - Services.AddTransient(); - Services.AddScoped(factory => new Paths(factory.GetService(), true)); - - // add dev server - Services.AddOptions() - .Bind(Configuration.GetSection("Zero:DevServer")) - .Configure((opts, env) => - { - opts.WorkingDirectory = Path.Combine(env.ContentRootPath, "..", "Zero.Web.UI", "App"); - }); - - Services.AddHostedService(); } diff --git a/zero.Core/_legacy/Api/ApplicationsApi.cs b/zero.Core/_legacy/Api/ApplicationsApi.cs index 8de5f6fb..5608a0a1 100644 --- a/zero.Core/_legacy/Api/ApplicationsApi.cs +++ b/zero.Core/_legacy/Api/ApplicationsApi.cs @@ -14,7 +14,7 @@ namespace zero.Core.Api IValidator Validator; - public ApplicationsApi(ICollectionContext store, IValidator validator) : base(store, isCoreDatabase: true) + public ApplicationsApi(IStoreContext store, IValidator validator) : base(store, isCoreDatabase: true) { Validator = validator; } diff --git a/zero.Core/_legacy/Api/BackofficeApi.cs b/zero.Core/_legacy/Api/BackofficeApi.cs index b82660d5..592dcecd 100644 --- a/zero.Core/_legacy/Api/BackofficeApi.cs +++ b/zero.Core/_legacy/Api/BackofficeApi.cs @@ -21,19 +21,19 @@ namespace zero.Core.Api const string NEW_ID = "new:"; - protected ICollectionContext Context { get; private set; } + protected IStoreContext Context { get; private set; } protected bool IsCoreDatabase { get; private set; } - public BackofficeApi(ICollectionContext context) + public BackofficeApi(IStoreContext context) { Context = context; Store = context.Store; IsCoreDatabase = false; } - internal BackofficeApi(ICollectionContext context, bool isCoreDatabase) : this(context) + internal BackofficeApi(IStoreContext context, bool isCoreDatabase) : this(context) { IsCoreDatabase = isCoreDatabase; } diff --git a/zero.Core/_legacy/Api/BackofficeStore.cs b/zero.Core/_legacy/Api/BackofficeStore.cs index 4cf65eaf..b5d6ae9d 100644 --- a/zero.Core/_legacy/Api/BackofficeStore.cs +++ b/zero.Core/_legacy/Api/BackofficeStore.cs @@ -12,12 +12,12 @@ namespace zero.Core.Api public IZeroOptions Options { get; private set; } - public IAuthenticationApi Auth { get; private set; } + public IAuthenticationService Auth { get; private set; } public IMessageAggregator Messages { get; private set; } - public BackofficeStore(IZeroStore store, IZeroContext context, IZeroOptions options, IAuthenticationApi authenticationApi, IMessageAggregator messages) + public BackofficeStore(IZeroStore store, IZeroContext context, IZeroOptions options, IAuthenticationService authenticationApi, IMessageAggregator messages) { Store = store; Context = context; @@ -36,7 +36,7 @@ namespace zero.Core.Api IZeroOptions Options { get; } - IAuthenticationApi Auth { get; } + IAuthenticationService Auth { get; } IMessageAggregator Messages { get; } } diff --git a/zero.Core/_legacy/Api/ModulesApi.cs b/zero.Core/_legacy/Api/ModulesApi.cs index 150bfac1..9f038912 100644 --- a/zero.Core/_legacy/Api/ModulesApi.cs +++ b/zero.Core/_legacy/Api/ModulesApi.cs @@ -16,7 +16,7 @@ namespace zero.Core.Api protected IHandlerHolder Handler { get; private set; } - public ModulesApi(IZeroOptions options, ICollectionContext store, IHandlerHolder handler) : base(store) + public ModulesApi(IZeroOptions options, IStoreContext store, IHandlerHolder handler) : base(store) { Options = options; Handler = handler; diff --git a/zero.Core/_legacy/Api/PreviewApi.cs b/zero.Core/_legacy/Api/PreviewApi.cs index 37d6ed6a..173eedb2 100644 --- a/zero.Core/_legacy/Api/PreviewApi.cs +++ b/zero.Core/_legacy/Api/PreviewApi.cs @@ -11,7 +11,7 @@ namespace zero.Core.Api { Preview Blueprint; - public PreviewApi(ICollectionContext store, Preview blueprint) : base(store) + public PreviewApi(IStoreContext store, Preview blueprint) : base(store) { Blueprint = blueprint; } diff --git a/zero.Core/_legacy/Api/RecycleBinApi.cs b/zero.Core/_legacy/Api/RecycleBinApi.cs index aae67416..4e654b47 100644 --- a/zero.Core/_legacy/Api/RecycleBinApi.cs +++ b/zero.Core/_legacy/Api/RecycleBinApi.cs @@ -15,7 +15,7 @@ namespace zero.Core.Api { RecycledEntity Blueprint; - public RecycleBinApi(ICollectionContext store, RecycledEntity blueprint) : base(store) + public RecycleBinApi(IStoreContext store, RecycledEntity blueprint) : base(store) { Blueprint = blueprint; } diff --git a/zero.Core/_legacy/Api/RevisionsApi.cs b/zero.Core/_legacy/Api/RevisionsApi.cs index aca17eca..08ea47c8 100644 --- a/zero.Core/_legacy/Api/RevisionsApi.cs +++ b/zero.Core/_legacy/Api/RevisionsApi.cs @@ -40,7 +40,7 @@ namespace zero.Core.Api // load affected users as the revisions could have been edited by other users too string[] userIds = items.Select(x => x.LastModifiedById).Distinct().ToArray(); - Dictionary users = await Session.LoadAsync(userIds); + Dictionary users = await Session.LoadAsync(userIds); // create revision objects foreach (T item in items) @@ -53,7 +53,7 @@ namespace zero.Core.Api Date = item.LastModifiedDate }; - if (!item.LastModifiedById.IsNullOrEmpty() && users.TryGetValue(item.LastModifiedById, out BackofficeUser user) && user != null) + if (!item.LastModifiedById.IsNullOrEmpty() && users.TryGetValue(item.LastModifiedById, out ZeroUser user) && user != null) { revision.User = new RevisionUser() { diff --git a/zero.Core/_legacy/Api/SetupApi.cs b/zero.Core/_legacy/Api/SetupApi.cs index 3be2ba30..760c4aaa 100644 --- a/zero.Core/_legacy/Api/SetupApi.cs +++ b/zero.Core/_legacy/Api/SetupApi.cs @@ -13,12 +13,12 @@ namespace zero.Core.Api { protected IZeroOptions Options { get; private set; } - protected IPasswordHasher PasswordHasher { get; private set; } + protected IPasswordHasher PasswordHasher { get; private set; } protected IZeroDocumentConventionsBuilder ConventionsBuilder { get; private set; } - public SetupApi(IZeroOptions options, IPasswordHasher passwordHasher, IZeroDocumentConventionsBuilder conventionsBuilder) + public SetupApi(IZeroOptions options, IPasswordHasher passwordHasher, IZeroDocumentConventionsBuilder conventionsBuilder) { Options = options; PasswordHasher = passwordHasher; @@ -64,7 +64,7 @@ namespace zero.Core.Api }); // create user - BackofficeUser user = Prepare(new BackofficeUser() + ZeroUser user = Prepare(new ZeroUser() { IsSuper = true, CreatedDate = DateTimeOffset.Now, @@ -112,9 +112,9 @@ namespace zero.Core.Api await session.StoreAsync(app); // save default user roles - IList roles = GetRoles(model); + IList roles = GetRoles(model); - foreach (BackofficeUserRole role in roles) + foreach (ZeroUserRole role in roles) { await session.StoreAsync(role); } @@ -221,11 +221,11 @@ namespace zero.Core.Api /// /// Create default roles /// - IList GetRoles(SetupModel model) + IList GetRoles(SetupModel model) { string type = Constants.Auth.Claims.Permission; - BackofficeUserRole adminRole = Prepare(new BackofficeUserRole() + ZeroUserRole adminRole = Prepare(new ZeroUserRole() { Name = "Administrator", Alias = Safenames.Alias("Administrator"), @@ -250,7 +250,7 @@ namespace zero.Core.Api }, }); - BackofficeUserRole editorRole = Prepare(new BackofficeUserRole() + ZeroUserRole editorRole = Prepare(new ZeroUserRole() { Name = "Editor", Alias = Safenames.Alias("Editor"), @@ -269,7 +269,7 @@ namespace zero.Core.Api } }); - BackofficeUserRole defaultRole = Prepare(new BackofficeUserRole() + ZeroUserRole defaultRole = Prepare(new ZeroUserRole() { Name = "Standard", Alias = Safenames.Alias("Standard"), @@ -283,7 +283,7 @@ namespace zero.Core.Api } }); - return new List() { adminRole, editorRole, defaultRole }; + return new List() { adminRole, editorRole, defaultRole }; } diff --git a/zero.Core/_legacy/Api/SpacesApi.cs b/zero.Core/_legacy/Api/SpacesApi.cs index 36a6d870..3f096bcb 100644 --- a/zero.Core/_legacy/Api/SpacesApi.cs +++ b/zero.Core/_legacy/Api/SpacesApi.cs @@ -14,10 +14,10 @@ namespace zero.Core.Api { public class SpacesApi : BackofficeApi, ISpacesApi { - protected IPermissionsApi PermissionsApi { get; private set; } + protected IPermissionsService PermissionsApi { get; private set; } - public SpacesApi(ICollectionContext store, IPermissionsApi permissionsApi) : base(store) + public SpacesApi(IStoreContext store, IPermissionsService permissionsApi) : base(store) { PermissionsApi = permissionsApi; } diff --git a/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs b/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs index 63ef5c6c..bf105bb3 100644 --- a/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs +++ b/zero.Core/_legacy/Collections/Integrations/IntegrationsCollection.cs @@ -9,7 +9,7 @@ using zero.Core.Integrations; namespace zero.Core.Collections { - public class IntegrationsCollection : EntityCollection, IIntegrationsCollection + public class IntegrationsCollection : EntityStore, IIntegrationsCollection { /// public IReadOnlyCollection RegisteredTypes { get; private set; } @@ -17,7 +17,7 @@ namespace zero.Core.Collections protected ILogger Logger { get; private set; } - public IntegrationsCollection(ICollectionContext context, ILogger logger) : base(context) + public IntegrationsCollection(IStoreContext context, ILogger logger) : base(context) { Options = new(true); RegisteredTypes = Context.Options.Integrations.GetAllItems(); diff --git a/zero.Core/_legacy/Collections/Media/MediaCollection.cs b/zero.Core/_legacy/Collections/Media/MediaCollection.cs index b5103d09..c8b3c6a6 100644 --- a/zero.Core/_legacy/Collections/Media/MediaCollection.cs +++ b/zero.Core/_legacy/Collections/Media/MediaCollection.cs @@ -16,12 +16,12 @@ using zero.Core.Database.Indexes; namespace zero.Core.Collections { - public class MediaCollection : EntityCollection, IMediaCollection + public class MediaCollection : EntityStore, IMediaCollection { /// public IMediaFolderCollection Folders { get; protected set; } - public MediaCollection(ICollectionContext context, IMediaFolderCollection folders, IPaths paths) : base(context) + public MediaCollection(IStoreContext context, IMediaFolderCollection folders, IPaths paths) : base(context) { Options = new(true); Folders = folders; @@ -241,7 +241,7 @@ namespace zero.Core.Collections } - public interface IMediaCollection : IEntityCollection + public interface IMediaCollection : IEntityStore { /// /// Media folder collection diff --git a/zero.Core/_legacy/Collections/Media/MediaFolderCollection.cs b/zero.Core/_legacy/Collections/Media/MediaFolderCollection.cs index 054c5f34..a2d61132 100644 --- a/zero.Core/_legacy/Collections/Media/MediaFolderCollection.cs +++ b/zero.Core/_legacy/Collections/Media/MediaFolderCollection.cs @@ -9,9 +9,9 @@ using zero.Core.Database.Indexes; namespace zero.Core.Collections { - public class MediaFolderCollection : EntityCollection, IMediaFolderCollection + public class MediaFolderCollection : EntityStore, IMediaFolderCollection { - public MediaFolderCollection(ICollectionContext context) : base(context) + public MediaFolderCollection(IStoreContext context) : base(context) { Options = new(true); } @@ -146,7 +146,7 @@ namespace zero.Core.Collections } - public interface IMediaFolderCollection : IEntityCollection + public interface IMediaFolderCollection : IEntityStore { /// /// Get hierarchy for a folder diff --git a/zero.Core/_legacy/Collections/Pages/PagesCollection.cs b/zero.Core/_legacy/Collections/Pages/PagesCollection.cs index baa8f8d0..da540a02 100644 --- a/zero.Core/_legacy/Collections/Pages/PagesCollection.cs +++ b/zero.Core/_legacy/Collections/Pages/PagesCollection.cs @@ -29,7 +29,7 @@ namespace zero.Core.Collections protected IRoutes Routes { get; set; } - public PagesCollection(ICollectionContext context, IRecycleBinApi recycleBinApi, IHandlerHolder handler, + public PagesCollection(IStoreContext context, IRecycleBinApi recycleBinApi, IHandlerHolder handler, ILogger logger, IRoutes routes, IValidator validator = null) : base(context, validator) { RecycleBinApi = recycleBinApi; diff --git a/zero.Core/_legacy/Collections/Translations/TranslationsCollection.cs b/zero.Core/_legacy/Collections/Translations/TranslationsCollection.cs index 3212d583..0f96d764 100644 --- a/zero.Core/_legacy/Collections/Translations/TranslationsCollection.cs +++ b/zero.Core/_legacy/Collections/Translations/TranslationsCollection.cs @@ -2,9 +2,9 @@ namespace zero.Core.Collections { - public class TranslationsCollection : EntityCollection, ITranslationsCollection + public class TranslationsCollection : EntityStore, ITranslationsCollection { - public TranslationsCollection(ICollectionContext context) : base(context) { } + public TranslationsCollection(IStoreContext context) : base(context) { } /// @@ -15,7 +15,7 @@ namespace zero.Core.Collections } - public interface ITranslationsCollection : IEntityCollection + public interface ITranslationsCollection : IEntityStore { /// /// Get a translated string by id diff --git a/zero.Core/zero.Core.csproj b/zero.Core/zero.Core.csproj index e8cffa71..dac059fc 100644 --- a/zero.Core/zero.Core.csproj +++ b/zero.Core/zero.Core.csproj @@ -28,8 +28,4 @@ - - - - \ No newline at end of file