diff --git a/zero.Core/Api/ApplicationContext.cs b/zero.Core/Api/ApplicationContext.cs index a465f3cd..99c8d219 100644 --- a/zero.Core/Api/ApplicationContext.cs +++ b/zero.Core/Api/ApplicationContext.cs @@ -11,6 +11,7 @@ using System.Security.Claims; using System.Threading.Tasks; using zero.Core.Entities; using zero.Core.Extensions; +using zero.Core.Handlers; using zero.Core.Identity; using zero.Core.Options; @@ -28,20 +29,23 @@ namespace zero.Core.Api protected IZeroOptions Options { get; private set; } - protected UserManager UserManager { get; private set; } + protected UserManager UserManager { get; private set; } protected ILogger Logger { get; private set; } + protected IApplicationResolverHandler Handler { get; private set; } + static IList Apps { get; set; } - public ApplicationContext(IDocumentStore raven, IZeroOptions options, UserManager userManager, ILogger logger) + public ApplicationContext(IDocumentStore raven, IZeroOptions options, UserManager userManager, ILogger logger, IApplicationResolverHandler handler = null) { Raven = raven; Options = options; UserManager = userManager; Logger = logger; + Handler = handler; } @@ -79,7 +83,7 @@ namespace zero.Core.Api /// - public async Task TrySwitchForUser(User user, string appId) + public async Task TrySwitchForUser(BackofficeUser user, string appId) { if (user == null || appId.IsNullOrEmpty()) { @@ -106,13 +110,13 @@ namespace zero.Core.Api /// public async Task ResolveFromUser(ClaimsPrincipal user) { - User userEntity = await UserManager.GetUserAsync(user); + BackofficeUser userEntity = await UserManager.GetUserAsync(user); return await ResolveFromUser(userEntity); } /// - public async Task ResolveFromUser(User user) + public async Task ResolveFromUser(BackofficeUser user) { if (user == null) { @@ -153,7 +157,7 @@ namespace zero.Core.Api /// public async Task ResolveFromRequest(HttpContext context) { - return await ResolveFromUri(context.Request.GetEncodedUrl()); + return Handler?.Resolve(context.Request, await GetApplications()) ?? await ResolveFromUri(context.Request.GetEncodedUrl()); } @@ -177,7 +181,7 @@ namespace zero.Core.Api using IAsyncDocumentSession session = Raven.OpenAsyncSession(); IApplication app = await session.LoadAsync(appId); - return new ApplicationContext(Raven, Options, UserManager, Logger) + return new ApplicationContext(Raven, Options, UserManager, Logger, Handler) { App = app, AppId = appId @@ -259,7 +263,7 @@ namespace zero.Core.Api /// /// Get applications the user has access to /// - string[] GetAllowedAppIdsForUser(User user) + string[] GetAllowedAppIdsForUser(BackofficeUser user) { IEnumerable permissions = user.Claims .Where(claim => claim.Type == Constants.Auth.Claims.Permission && claim.Value.StartsWith(Permissions.Applications)) @@ -295,7 +299,7 @@ namespace zero.Core.Api /// /// Try to switch the current application for a user /// - Task TrySwitchForUser(User user, string appId); + Task TrySwitchForUser(BackofficeUser user, string appId); /// /// Resolves the current application from the request path @@ -322,7 +326,7 @@ namespace zero.Core.Api /// Resolves the current application from a user. /// This method won't return apps the user has no access to. /// - Task ResolveFromUser(User user); + Task ResolveFromUser(BackofficeUser user); /// /// Creates a new application context for the specified application. diff --git a/zero.Core/Api/AuthenticationApi.cs b/zero.Core/Api/AuthenticationApi.cs index 57c0c09f..6c2b6bca 100644 --- a/zero.Core/Api/AuthenticationApi.cs +++ b/zero.Core/Api/AuthenticationApi.cs @@ -15,10 +15,10 @@ namespace zero.Core.Api protected IZeroContext Context { get; set; } - protected SignInManager SignInManager { get; private set; } + protected SignInManager SignInManager { get; private set; } - public AuthenticationApi(IDocumentStore raven, IZeroContext context, SignInManager signInManager) + public AuthenticationApi(IDocumentStore raven, IZeroContext context, SignInManager signInManager) { Raven = raven; Context = context; @@ -48,7 +48,7 @@ namespace zero.Core.Api /// - public async Task GetUser() + public async Task GetUser() { return await SignInManager.UserManager.GetUserAsync(Context.User); } @@ -77,7 +77,7 @@ namespace zero.Core.Api { EntityResult result = new EntityResult(); - User user = await SignInManager.UserManager.FindByNameAsync(email); + BackofficeUser user = await SignInManager.UserManager.FindByNameAsync(email); if (user == null) { @@ -140,7 +140,7 @@ namespace zero.Core.Api /// /// Get currently logged-in user /// - Task GetUser(); + Task GetUser(); /// /// Whether a user is currently logged-in diff --git a/zero.Core/Api/AuthorizationApi.cs b/zero.Core/Api/AuthorizationApi.cs index 6e3e2623..1dc8f968 100644 --- a/zero.Core/Api/AuthorizationApi.cs +++ b/zero.Core/Api/AuthorizationApi.cs @@ -18,7 +18,7 @@ namespace zero.Core.Api protected IHttpContextAccessor HttpContextAccessor { get; set; } - protected SignInManager SignInManager { get; private set; } + protected SignInManager SignInManager { get; private set; } protected ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; @@ -26,7 +26,7 @@ namespace zero.Core.Api static Type AppAwareShareableType = typeof(IAppAwareShareableEntity); - public AuthorizationApi(IDocumentStore raven, IHttpContextAccessor httpContextAccessor, SignInManager signInManager) + public AuthorizationApi(IDocumentStore raven, IHttpContextAccessor httpContextAccessor, SignInManager signInManager) { Raven = raven; HttpContextAccessor = httpContextAccessor; diff --git a/zero.Core/Api/RevisionsApi.cs b/zero.Core/Api/RevisionsApi.cs index fb379166..7fc0cb55 100644 --- a/zero.Core/Api/RevisionsApi.cs +++ b/zero.Core/Api/RevisionsApi.cs @@ -61,7 +61,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) @@ -73,7 +73,7 @@ namespace zero.Core.Api Json = includeContent ? JsonConvert.SerializeObject(item) : null }; - if (!item.LastModifiedById.IsNullOrEmpty() && users.TryGetValue(item.LastModifiedById, out User user)) + if (!item.LastModifiedById.IsNullOrEmpty() && users.TryGetValue(item.LastModifiedById, out BackofficeUser user)) { revision.User = new RevisionUser() { diff --git a/zero.Core/Api/SetupApi.cs b/zero.Core/Api/SetupApi.cs index 6057152c..8708a33a 100644 --- a/zero.Core/Api/SetupApi.cs +++ b/zero.Core/Api/SetupApi.cs @@ -23,10 +23,10 @@ namespace zero.Core.Api { protected IZeroOptions Options { get; private set; } - protected UserManager UserManager { get; private set; } + protected UserManager UserManager { get; private set; } - public SetupApi(IZeroOptions options, UserManager userManager) + public SetupApi(IZeroOptions options, UserManager userManager) { Options = options; UserManager = userManager; @@ -68,7 +68,7 @@ namespace zero.Core.Api }; // create user - User user = new User() + BackofficeUser user = new BackofficeUser() { IsSuper = true, CreatedDate = DateTimeOffset.Now, @@ -117,16 +117,16 @@ namespace zero.Core.Api await session.StoreAsync(user); // save default user roles - IList roles = GetRoles(model); + IList roles = GetRoles(model); - foreach (UserRole role in roles) + foreach (BackofficeUserRole role in roles) { await session.StoreAsync(role); } // add admin role to super user - user.Roles.Add(roles.First(role => role.Name == "Standard").Alias); - user.Roles.Add(roles.First(role => role.Name == "Administrator").Alias); + user.RoleIds.Add(roles.First(role => role.Name == "Standard").Alias); + user.RoleIds.Add(roles.First(role => role.Name == "Administrator").Alias); // create language await session.StoreAsync(language); @@ -224,11 +224,11 @@ namespace zero.Core.Api /// /// Create default roles /// - IList GetRoles(SetupModel model) + IList GetRoles(SetupModel model) { string type = Constants.Auth.Claims.Permission; - UserRole adminRole = new UserRole() + BackofficeUserRole adminRole = new BackofficeUserRole() { Name = "Administrator", Alias = Safenames.Alias("Administrator"), @@ -254,7 +254,7 @@ namespace zero.Core.Api }, }; - UserRole editorRole = new UserRole() + BackofficeUserRole editorRole = new BackofficeUserRole() { Name = "Editor", Alias = Safenames.Alias("Editor"), @@ -274,7 +274,7 @@ namespace zero.Core.Api } }; - UserRole defaultRole = new UserRole() + BackofficeUserRole defaultRole = new BackofficeUserRole() { Name = "Standard", Alias = Safenames.Alias("Standard"), @@ -289,7 +289,7 @@ namespace zero.Core.Api } }; - return new List() { adminRole, editorRole, defaultRole }; + return new List() { adminRole, editorRole, defaultRole }; } } diff --git a/zero.Core/Api/UserApi.cs b/zero.Core/Api/UserApi.cs index 26f19f9b..39192f7a 100644 --- a/zero.Core/Api/UserApi.cs +++ b/zero.Core/Api/UserApi.cs @@ -12,11 +12,11 @@ namespace zero.Core.Api { public class UserApi : AppAwareBackofficeApi, IUserApi { - protected UserManager UserManager { get; private set; } + protected UserManager UserManager { get; private set; } protected IZeroContext Context { get; set; } - public UserApi(IBackofficeStore store, UserManager userManager, IZeroContext context) : base(store) + public UserApi(IBackofficeStore store, UserManager userManager, IZeroContext context) : base(store) { UserManager = userManager; Context = context; @@ -24,33 +24,33 @@ namespace zero.Core.Api /// - public async Task GetUserById(string id) + public async Task GetUserById(string id) { - IUser user = await UserManager.FindByIdAsync(id); + IBackofficeUser user = await UserManager.FindByIdAsync(id); return user; } /// - public async Task GetUserByEmail(string email) + public async Task GetUserByEmail(string email) { - IUser user = await UserManager.FindByEmailAsync(email); + IBackofficeUser user = await UserManager.FindByEmailAsync(email); return user; } /// - public async Task> GetByIds(params string[] ids) + public async Task> GetByIds(params string[] ids) { - return await GetByIds(ids); + return await GetByIds(ids); } /// - public async Task> GetAll() + public async Task> GetAll() { using IAsyncDocumentSession session = Raven.OpenAsyncSession(); - return await session.Query() + return await session.Query() .Scope(Scope) .OrderByDescending(x => x.CreatedDate) .ToListAsync(); @@ -58,7 +58,7 @@ namespace zero.Core.Api /// - public async Task> GetByQuery(ListQuery query) + public async Task> GetByQuery(ListQuery query) { string currentUserId = UserManager.GetUserId(Context.User); HashSet appIds = new HashSet() { Constants.Database.SharedAppId, Scope.AppId }; @@ -66,44 +66,44 @@ namespace zero.Core.Api query.SearchSelector = user => user.Name; using IAsyncDocumentSession session = Raven.OpenAsyncSession(); - return await session.Query() + return await session.Query() .Where(x => x.AppId.In(appIds) || x.Id == currentUserId) .ToQueriedListAsync(query); } /// - public async Task> Save(IUser model) + public async Task> Save(IBackofficeUser model) { return await SaveModel(model); //, new UserValidator()); } /// - public async Task> Delete(string id) + public async Task> Delete(string id) { - return await DeleteById(id); + return await DeleteById(id); } /// - public async Task> UpdatePassword(IUser user, string currentPassword, string newPassword) + public async Task> UpdatePassword(IBackofficeUser user, string currentPassword, string newPassword) { if (currentPassword.IsNullOrWhiteSpace() || newPassword.IsNullOrWhiteSpace()) { - return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); + return EntityResult.Fail(nameof(currentPassword), "@errors.changepassword.emptyfields"); } if (user == null) { - return EntityResult.Fail("@errors.changepassword.nouser"); + return EntityResult.Fail("@errors.changepassword.nouser"); } - IdentityResult identityResult = await UserManager.ChangePasswordAsync(user as User, currentPassword, newPassword); + IdentityResult identityResult = await UserManager.ChangePasswordAsync(user as BackofficeUser, currentPassword, newPassword); if (!identityResult.Succeeded) { - EntityResult result = EntityResult.Fail(); + EntityResult result = EntityResult.Fail(); foreach (IdentityError error in identityResult.Errors) { @@ -113,19 +113,19 @@ namespace zero.Core.Api return result; } - return EntityResult.Success(user); + return EntityResult.Success(user); } /// - public async Task> Enable(IUser user) + public async Task> Enable(IBackofficeUser user) { return await UpdateActiveState(user, true); } /// - public async Task> Disable(IUser user) + public async Task> Disable(IBackofficeUser user) { return await UpdateActiveState(user, false); } @@ -135,15 +135,15 @@ namespace zero.Core.Api /// Updates the active state of user. /// If IsActive=false, the user cannot login anymore /// - async Task> UpdateActiveState(IUser user, bool isActive) + async Task> UpdateActiveState(IBackofficeUser user, bool isActive) { user.IsActive = isActive; - IdentityResult identityResult = await UserManager.UpdateAsync(user as User); + IdentityResult identityResult = await UserManager.UpdateAsync(user as BackofficeUser); if (!identityResult.Succeeded) { - EntityResult result = EntityResult.Fail(); + EntityResult result = EntityResult.Fail(); foreach (IdentityError error in identityResult.Errors) { @@ -153,9 +153,9 @@ namespace zero.Core.Api return result; } - await UserManager.UpdateSecurityStampAsync(user as User); + await UserManager.UpdateSecurityStampAsync(user as BackofficeUser); - return EntityResult.Success(user); + return EntityResult.Success(user); } } @@ -165,52 +165,52 @@ namespace zero.Core.Api /// /// Find user by id /// - Task GetUserById(string id); + Task GetUserById(string id); /// /// Find user by email /// - Task GetUserByEmail(string email); + Task GetUserByEmail(string email); /// /// Get users by ids /// - Task> GetByIds(params string[] ids); + Task> GetByIds(params string[] ids); /// /// Get all users for the selected application /// - Task> GetAll(); + Task> GetAll(); /// /// Get all available users (with query) /// - Task> GetByQuery(ListQuery query); + Task> GetByQuery(ListQuery query); /// /// Creates or updates a user /// - Task> Save(IUser model); + Task> Save(IBackofficeUser model); /// /// Deletes a user /// - Task> Delete(string id); + Task> Delete(string id); /// /// Changes the password of the current user. /// User is logged out if this operation succeeds. /// - Task> UpdatePassword(IUser user, string currentPassword, string newPassword); + Task> UpdatePassword(IBackofficeUser user, string currentPassword, string newPassword); /// /// Enables a user /// - Task> Enable(IUser user); + Task> Enable(IBackofficeUser user); /// /// Disables a user /// - Task> Disable(IUser user); + Task> Disable(IBackofficeUser user); } } diff --git a/zero.Core/Api/UserRolesApi.cs b/zero.Core/Api/UserRolesApi.cs index b80a742e..67cd186f 100644 --- a/zero.Core/Api/UserRolesApi.cs +++ b/zero.Core/Api/UserRolesApi.cs @@ -19,14 +19,14 @@ namespace zero.Core.Api { protected IHttpContextAccessor HttpContextAccessor { get; set; } - protected UserManager UserManager { get; private set; } + protected UserManager UserManager { get; private set; } - protected RoleManager RoleManager { get; private set; } + protected RoleManager RoleManager { get; private set; } private ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; - public UserRolesApi(IHttpContextAccessor httpContextAccessor, UserManager userManager, RoleManager roleManager, IBackofficeStore store) : base(store) + public UserRolesApi(IHttpContextAccessor httpContextAccessor, UserManager userManager, RoleManager roleManager, IBackofficeStore store) : base(store) { HttpContextAccessor = httpContextAccessor; UserManager = userManager; @@ -35,30 +35,30 @@ namespace zero.Core.Api /// - public async Task> GetAll() + public async Task> GetAll() { using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) { - return await session.Query().OrderBy(x => x.Sort).ThenBy(x => x.Name).ToListAsync(); + return await session.Query().OrderBy(x => x.Sort).ThenBy(x => x.Name).ToListAsync(); } } /// - public async Task GetById(string id) + public async Task GetById(string id) { return await RoleManager.FindByIdAsync(id); } /// - public async Task> Save(IUserRole model) + public async Task> Save(IBackofficeUserRole model) { ValidationResult validation = await new UserRoleValidator().ValidateAsync(model); if (!validation.IsValid) { - return EntityResult.Fail(validation); + return EntityResult.Fail(validation); } if (model.Id.IsNullOrEmpty()) @@ -83,20 +83,20 @@ namespace zero.Core.Api } } - return EntityResult.Success(model); + return EntityResult.Success(model); } /// - public async Task> Delete(string id) + public async Task> Delete(string id) { using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) { - UserRole country = await session.LoadAsync(id); + BackofficeUserRole country = await session.LoadAsync(id); if (country == null) { - return EntityResult.Fail("@errors.ondelete.idnotfound"); + return EntityResult.Fail("@errors.ondelete.idnotfound"); } session.Delete(country); @@ -104,7 +104,7 @@ namespace zero.Core.Api await session.SaveChangesAsync(); } - return EntityResult.Success(); + return EntityResult.Success(); } } @@ -114,21 +114,21 @@ namespace zero.Core.Api /// /// Get all user roles /// - Task> GetAll(); + Task> GetAll(); /// /// Get role by id /// - Task GetById(string id); + Task GetById(string id); /// /// Create or update a role /// - Task> Save(IUserRole model); + Task> Save(IBackofficeUserRole model); /// /// Deletes a role /// - Task> Delete(string id); + Task> Delete(string id); } } diff --git a/zero.Core/Entities/User/User.cs b/zero.Core/Entities/User/BackofficeUser.cs similarity index 87% rename from zero.Core/Entities/User/User.cs rename to zero.Core/Entities/User/BackofficeUser.cs index e2219337..926a523c 100644 --- a/zero.Core/Entities/User/User.cs +++ b/zero.Core/Entities/User/BackofficeUser.cs @@ -4,7 +4,7 @@ using zero.Core.Attributes; namespace zero.Core.Entities { - public class User : ZeroEntity, IUser + public class BackofficeUser : ZeroEntity, IBackofficeUser { /// public string Username { get; set; } @@ -36,7 +36,7 @@ namespace zero.Core.Entities /// - public List Roles { get; set; } = new List(); + public List RoleIds { get; set; } = new List(); /// public List Claims { get; set; } = new List(); @@ -73,8 +73,8 @@ namespace zero.Core.Entities } - [Collection("Users")] - public interface IUser : IZeroEntity, IAppAwareEntity, IZeroDbConventions, IIdentityUserWithRoles + [Collection("BackofficeUsers")] + public interface IBackofficeUser : IZeroEntity, IAppAwareEntity, IZeroDbConventions, IIdentityUserWithRoles { /// /// Currently selected app id for the backoffice diff --git a/zero.Core/Entities/User/UserRole.cs b/zero.Core/Entities/User/BackofficeUserRole.cs similarity index 69% rename from zero.Core/Entities/User/UserRole.cs rename to zero.Core/Entities/User/BackofficeUserRole.cs index dd895420..4a611105 100644 --- a/zero.Core/Entities/User/UserRole.cs +++ b/zero.Core/Entities/User/BackofficeUserRole.cs @@ -3,7 +3,7 @@ using zero.Core.Attributes; namespace zero.Core.Entities { - public class UserRole : ZeroEntity, IUserRole, IZeroDbConventions + public class BackofficeUserRole : ZeroEntity, IBackofficeUserRole, IZeroDbConventions { /// public string Description { get; set; } @@ -16,8 +16,8 @@ namespace zero.Core.Entities } - [Collection("UserRoles")] - public interface IUserRole : IZeroEntity, IAppAwareShareableEntity, IZeroDbConventions, IIdentityUserRole + [Collection("BackofficeUserRoles")] + public interface IBackofficeUserRole : IZeroEntity, IAppAwareShareableEntity, IZeroDbConventions, IIdentityUserRole { /// /// Additional description diff --git a/zero.Core/Entities/User/IIdentityUser.cs b/zero.Core/Entities/User/IIdentityUser.cs index 3ed0f75f..4e42d49e 100644 --- a/zero.Core/Entities/User/IIdentityUser.cs +++ b/zero.Core/Entities/User/IIdentityUser.cs @@ -8,7 +8,7 @@ namespace zero.Core.Entities /// /// The roles (aliases) of the user /// - List Roles { get; set; } + List RoleIds { get; set; } } diff --git a/zero.Core/Handlers/IApplicationResolverHandler.cs b/zero.Core/Handlers/IApplicationResolverHandler.cs new file mode 100644 index 00000000..08e94b39 --- /dev/null +++ b/zero.Core/Handlers/IApplicationResolverHandler.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using zero.Core.Entities; + +namespace zero.Core.Handlers +{ + public interface IApplicationResolverHandler : IHandler + { + IApplication Resolve(HttpRequest request, IList applications); + } +} diff --git a/zero.Core/Handlers/IHandler.cs b/zero.Core/Handlers/IHandler.cs new file mode 100644 index 00000000..c0c06e5a --- /dev/null +++ b/zero.Core/Handlers/IHandler.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace zero.Core.Handlers +{ + public interface IHandler + { + } +} diff --git a/zero.Core/Handlers/IHandlerScope.cs b/zero.Core/Handlers/IHandlerScope.cs new file mode 100644 index 00000000..a8acf615 --- /dev/null +++ b/zero.Core/Handlers/IHandlerScope.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace zero.Core.Handlers +{ + public interface IHandlerScope where T : IHandler + { + } +} diff --git a/zero.Core/Identity/UserStore.Role.cs b/zero.Core/Identity/UserStore.Role.cs index ab5267fd..2aa34b99 100644 --- a/zero.Core/Identity/UserStore.Role.cs +++ b/zero.Core/Identity/UserStore.Role.cs @@ -11,6 +11,9 @@ using zero.Core.Entities; namespace zero.Core.Identity { + // TODO this class still searches for role names in role IDs, which is not correct. + // we need a join here + // guess this code will only be used in certain Authorize attributes public partial class RavenUserStore : IUserRoleStore where TUser : class, IIdentityUserWithRoles where TRole : class, IIdentityUserRole @@ -18,7 +21,7 @@ namespace zero.Core.Identity /// public Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - user.Roles.Add(roleName); + user.RoleIds.Add(roleName); return Task.CompletedTask; } @@ -26,7 +29,7 @@ namespace zero.Core.Identity /// public Task> GetRolesAsync(TUser user, CancellationToken cancellationToken) { - return Task.FromResult((IList)user.Roles.ToList()); + return Task.FromResult((IList)user.RoleIds.ToList()); } @@ -35,7 +38,7 @@ namespace zero.Core.Identity { using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) { - return await session.Query().Where(x => roleName.In(x.Roles)).ToListAsync(); + return await session.Query().Where(x => roleName.In(x.RoleIds)).ToListAsync(); } } @@ -43,14 +46,14 @@ namespace zero.Core.Identity /// public Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - return Task.FromResult(user.Roles.Contains(roleName, StringComparer.InvariantCultureIgnoreCase)); + return Task.FromResult(user.RoleIds.Contains(roleName, StringComparer.InvariantCultureIgnoreCase)); } /// public Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - user.Roles.Remove(roleName); + user.RoleIds.Remove(roleName); return Task.CompletedTask; } } diff --git a/zero.Core/Routing/Routes.cs b/zero.Core/Routing/Routes.cs index 577c57fa..bf50edc7 100644 --- a/zero.Core/Routing/Routes.cs +++ b/zero.Core/Routing/Routes.cs @@ -105,6 +105,11 @@ namespace zero.Core.Routing IApplication app = await Context.ResolveFromRequest(context); string path = context.Request.Path; + if (app == null) + { + return null; + } + return await ResolveUrl(app.Id, path); } diff --git a/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs b/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs index a56d9713..05f2a6a7 100644 --- a/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs +++ b/zero.Core/Security/ZeroBackofficeClaimsPrincipalFactory.cs @@ -11,8 +11,8 @@ using zero.Core.Identity; namespace zero.Core.Security { public class ZeroBackofficeClaimsPrincipalFactory : ZeroClaimsPrinicipalFactory - where TUser : class, IUser - where TRole : class, IUserRole + where TUser : class, IBackofficeUser + where TRole : class, IBackofficeUserRole { public ZeroBackofficeClaimsPrincipalFactory(UserManager userManager, RoleManager roleManager, IOptions optionsAccessor, IOptions> authOptions) : base(userManager, roleManager, optionsAccessor, authOptions) @@ -66,13 +66,6 @@ namespace zero.Core.Security claims.Add(new Claim(Constants.Auth.Claims.AppId, currentAppId)); claims.Add(new Claim(Constants.Auth.Claims.DefaultAppId, user.AppId)); - - // add default role when user has none - if (!claims.Any(x => x.Type == Constants.Auth.Claims.Role)) - { - claims.Add(new Claim(Constants.Auth.Claims.Role, "userRoles.1-A")); // TODO this needs to be dynamic - } - return claims; } } diff --git a/zero.Core/Validation/BackofficeUserValidator.cs b/zero.Core/Validation/BackofficeUserValidator.cs index 2a23a6e9..8a99818f 100644 --- a/zero.Core/Validation/BackofficeUserValidator.cs +++ b/zero.Core/Validation/BackofficeUserValidator.cs @@ -4,7 +4,7 @@ using zero.Core.Extensions; namespace zero.Core.Validation { - public class BackofficeUserValidator : ZeroValidator + public class BackofficeUserValidator : ZeroValidator { public BackofficeUserValidator(bool isCreate = false) { @@ -12,7 +12,7 @@ namespace zero.Core.Validation RuleFor(x => x.Email).NotEmpty().Email().MaximumLength(120); RuleFor(x => x.PasswordHash).NotEmpty(); RuleFor(x => x.LanguageId).NotEmpty(); - RuleFor(x => x.Roles).NotEmpty(); + RuleFor(x => x.RoleIds).NotEmpty(); if (isCreate) { diff --git a/zero.Core/Validation/UserRoleValidator.cs b/zero.Core/Validation/UserRoleValidator.cs index 0ca8da25..e90eb872 100644 --- a/zero.Core/Validation/UserRoleValidator.cs +++ b/zero.Core/Validation/UserRoleValidator.cs @@ -6,7 +6,7 @@ using System; namespace zero.Core.Validation { - public class UserRoleValidator : ZeroValidator + public class UserRoleValidator : ZeroValidator { const string SECTION_CLAIM = "section."; diff --git a/zero.Debug/Controllers/MigrationController.cs b/zero.Debug/Controllers/MigrationController.cs index abb29202..80be07f4 100644 --- a/zero.Debug/Controllers/MigrationController.cs +++ b/zero.Debug/Controllers/MigrationController.cs @@ -57,8 +57,8 @@ namespace zero.Debug.Controllers SpaceContents = await Handle(), TaxRates = await Handle(), Translations = await Handle(), - UserRoles = await Handle(), - Users = await Handle(), + UserRoles = await Handle(), + Users = await Handle(), }); } diff --git a/zero.Web/Controllers/AuthenticationController.cs b/zero.Web/Controllers/AuthenticationController.cs index b248d4b6..7a4255dd 100644 --- a/zero.Web/Controllers/AuthenticationController.cs +++ b/zero.Web/Controllers/AuthenticationController.cs @@ -21,7 +21,7 @@ namespace zero.Web.Controllers } - public async Task> GetUser() => Edit(await Api.GetUser()); + public async Task> GetUser() => Edit(await Api.GetUser()); public EntityResult IsLoggedIn() => EntityResult.Maybe(Api.IsLoggedIn()); @@ -42,7 +42,7 @@ namespace zero.Web.Controllers [HttpPost, ZeroAuthorize] public async Task SwitchApp(string appId) { - User user = await Api.GetUser(); + BackofficeUser user = await Api.GetUser(); return EntityResult.Maybe(await AppContext.TrySwitchForUser(user, appId)); } } diff --git a/zero.Web/Controllers/UserRolesController.cs b/zero.Web/Controllers/UserRolesController.cs index 069dbf37..50755c7c 100644 --- a/zero.Web/Controllers/UserRolesController.cs +++ b/zero.Web/Controllers/UserRolesController.cs @@ -22,20 +22,20 @@ namespace zero.Web.Controllers } - 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] IUserRole model) => await Api.Save(model); + public async Task> Save([FromBody] IBackofficeUserRole 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.Web/Controllers/UsersController.cs b/zero.Web/Controllers/UsersController.cs index 6c260b2c..ed4f27de 100644 --- a/zero.Web/Controllers/UsersController.cs +++ b/zero.Web/Controllers/UsersController.cs @@ -25,10 +25,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] ListQuery query) => await Api.GetByQuery(query); + public async Task> GetAll([FromQuery] ListQuery query) => await Api.GetByQuery(query); public IList GetAllPermissions() => PermissionsApi.GetAll(); @@ -54,18 +54,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 { - User user = await AuthenticationApi.GetUser(); - result = await Api.UpdatePassword(user as IUser, model.CurrentPassword, model.NewPassword); + BackofficeUser user = await AuthenticationApi.GetUser(); + result = await Api.UpdatePassword(user as IBackofficeUser, model.CurrentPassword, model.NewPassword); if (result.IsSuccess) { @@ -78,31 +78,31 @@ namespace zero.Web.Controllers [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Disable([FromBody] IUser model) + public async Task> Disable([FromBody] IBackofficeUser model) { - IUser entity = await Api.GetUserById(model.Id); + IBackofficeUser entity = await Api.GetUserById(model.Id); return await Api.Disable(entity); } [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Enable([FromBody] IUser model) + public async Task> Enable([FromBody] IBackofficeUser model) { - IUser entity = await Api.GetUserById(model.Id); + IBackofficeUser entity = await Api.GetUserById(model.Id); return await Api.Enable(entity); } [ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)] - public async Task> Save([FromBody] IUser model) => await Api.Save(model); + public async Task> Save([FromBody] IBackofficeUser 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] IUser model) => await Api.Save(model); + public async Task> SaveCurrent([FromBody] IBackofficeUser 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.Web/Defaults/ZeroBackofficePlugin.cs b/zero.Web/Defaults/ZeroBackofficePlugin.cs index b561be59..ef6fe215 100644 --- a/zero.Web/Defaults/ZeroBackofficePlugin.cs +++ b/zero.Web/Defaults/ZeroBackofficePlugin.cs @@ -65,8 +65,8 @@ namespace zero.Web.Defaults services.AddTransient, PageValidator>(); services.AddTransient, MediaValidator>(); services.AddTransient, MediaFolderValidator>(); - services.AddTransient, UserRoleValidator>(); - services.AddTransient, BackofficeUserValidator>(); + services.AddTransient, UserRoleValidator>(); + services.AddTransient, BackofficeUserValidator>(); services.AddTransient(); services.AddTransient(); diff --git a/zero.Web/Security/AuthenticationBuilderExtensions.cs b/zero.Web/Security/AuthenticationBuilderExtensions.cs index 231d8f84..9b2cf8e9 100644 --- a/zero.Web/Security/AuthenticationBuilderExtensions.cs +++ b/zero.Web/Security/AuthenticationBuilderExtensions.cs @@ -16,8 +16,8 @@ namespace zero.Web.Security public static class AuthenticationBuilderExtensions { public static AuthenticationBuilder AddZeroBackofficeCookie(this AuthenticationBuilder builder, Action> setupAction = null) - where TUser : class, IUser - where TRole : class, IUserRole + where TUser : class, IBackofficeUser + where TRole : class, IBackofficeUserRole { return builder.AddCookie(Constants.Auth.BackofficeScheme, Constants.Auth.BackofficeDisplayName, true, b => { diff --git a/zero.Web/ZeroBuilder.cs b/zero.Web/ZeroBuilder.cs index cfe0abf2..8401e94d 100644 --- a/zero.Web/ZeroBuilder.cs +++ b/zero.Web/ZeroBuilder.cs @@ -19,6 +19,7 @@ using zero.Core.Api; using zero.Core.Assemblies; using zero.Core.Entities; using zero.Core.Extensions; +using zero.Core.Handlers; using zero.Core.Identity; using zero.Core.Options; using zero.Core.Plugins; @@ -119,7 +120,6 @@ namespace zero.Web return new Paths(env.WebRootPath, true); }); - // add dev server Services.AddOptions() .Bind(Configuration.GetSection("Zero:DevServer")) @@ -171,11 +171,11 @@ namespace zero.Web { Services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureCookieAuthenticationOptions>()); - Services.AddZeroIdentity(); - Services.Replace, ZeroBackofficeClaimsPrincipalFactory>(); + Services.AddZeroIdentity(); + Services.Replace, ZeroBackofficeClaimsPrincipalFactory>(); Services.AddAuthentication(Constants.Auth.BackofficeScheme) - .AddZeroBackofficeCookie(); + .AddZeroBackofficeCookie(); Services.AddAuthorization(); } diff --git a/zero.Web/ZeroVue.cs b/zero.Web/ZeroVue.cs index 62a6dc53..2be3247e 100644 --- a/zero.Web/ZeroVue.cs +++ b/zero.Web/ZeroVue.cs @@ -64,7 +64,7 @@ namespace zero.Web config.AppId = AppContext.AppId; config.SharedAppId = Constants.Database.SharedAppId; - User user = await AuthenticationApi.GetUser(); + BackofficeUser user = await AuthenticationApi.GetUser(); if (user != null) { @@ -75,7 +75,7 @@ namespace zero.Web Email = user.Email, IsSuper = user.IsSuper, Name = user.Name, - Roles = user.Roles + Roles = user.RoleIds }; }