diff --git a/zero.Core/Api/SettingsApi.cs b/zero.Core/Api/SettingsApi.cs index 3662d597..9d7d8dfb 100644 --- a/zero.Core/Api/SettingsApi.cs +++ b/zero.Core/Api/SettingsApi.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.Options; using System.Collections.Generic; using zero.Core.Entities; +using zero.Core.Identity; namespace zero.Core.Api { diff --git a/zero.Core/Api/SetupApi.cs b/zero.Core/Api/SetupApi.cs index 8f2cefd7..8f19efab 100644 --- a/zero.Core/Api/SetupApi.cs +++ b/zero.Core/Api/SetupApi.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using zero.Core.Entities; using zero.Core.Entities.Setup; +using zero.Core.Identity; using zero.Core.Validation; @@ -99,16 +100,26 @@ namespace zero.Core.Api return entityResult; } - // save application and user + // save entities using (IAsyncDocumentSession session = raven.OpenAsyncSession()) { await session.StoreAsync(app); - // set app-id for user + // set app-id for user and store it user.AppId = session.Advanced.GetDocumentId(app); - await session.StoreAsync(user); + // save default user roles + IList roles = GetRoles(model); + + foreach (UserRole role in roles) + { + await session.StoreAsync(role); + } + + // add admin role to super user + user.Roles.Add(roles.First(role => role.Name == "Administrator").Alias); + // set countries using (Raven.Client.Documents.BulkInsert.BulkInsertOperation bulkInsert = raven.BulkInsert()) { @@ -192,6 +203,78 @@ namespace zero.Core.Api return countries; } + + + /// + /// Create default roles + /// + IList GetRoles(SetupModel model) + { + string type = Constants.Auth.Claims.Permission; + + UserRole adminRole = new UserRole() + { + Name = "Administrator", + Alias = Alias.Generate("Administrator"), + Sort = 0, + AppId = Constants.Database.SharedAppId, + Icon = "fth-award color-yellow", + CreatedDate = DateTimeOffset.Now, + IsActive = true, + Claims = new List() + { + new UserClaim(type, Permissions.Applications, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Dashboard, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Lists, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Pages, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Media, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Settings, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Applications, PermissionsValue.None), + new UserClaim(type, Permissions.Settings.Countries, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Logging, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Translations, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Updates, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Users, PermissionsValue.Write), + }, + }; + + UserRole editorRole = new UserRole() + { + Name = "Editor", + Alias = Alias.Generate("Editor"), + Sort = 1, + AppId = Constants.Database.SharedAppId, + Icon = "fth-feather", + CreatedDate = DateTimeOffset.Now, + IsActive = true, + Claims = new List() + { + new UserClaim(type, Permissions.Sections.Dashboard, PermissionsValue.Read), + new UserClaim(type, Permissions.Sections.Lists, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Pages, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Media, PermissionsValue.Write), + new UserClaim(type, Permissions.Sections.Settings, PermissionsValue.Write), + new UserClaim(type, Permissions.Settings.Translations, PermissionsValue.Write) + } + }; + + UserRole defaultRole = new UserRole() + { + Name = "Standard", + Alias = Alias.Generate("Standard"), + Sort = 2, + AppId = Constants.Database.SharedAppId, + Icon = "fth-users", + CreatedDate = DateTimeOffset.Now, + IsActive = true, + Claims = new List() + { + new UserClaim(type, Permissions.Sections.Dashboard, PermissionsValue.Read) + } + }; + + return new List() { adminRole, editorRole, defaultRole }; + } } diff --git a/zero.Core/Api/UserApi.cs b/zero.Core/Api/UserApi.cs index 0f7055e8..22b7cc66 100644 --- a/zero.Core/Api/UserApi.cs +++ b/zero.Core/Api/UserApi.cs @@ -1,8 +1,12 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Raven.Client.Documents; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; using zero.Core.Entities; +using zero.Core.Identity; namespace zero.Core.Api { @@ -14,6 +18,8 @@ namespace zero.Core.Api protected UserManager UserManager { get; private set; } + private ClaimsPrincipal Principal => HttpContextAccessor.HttpContext?.User; + public UserApi(IDocumentStore raven, IHttpContextAccessor httpContextAccessor, UserManager userManager) { @@ -22,6 +28,7 @@ namespace zero.Core.Api UserManager = userManager; } + /// public async Task GetUser() { @@ -44,6 +51,31 @@ namespace zero.Core.Api User user = await UserManager.FindByEmailAsync(email); return user; } + + + /// + public bool IsSuper() + { + return false; // TODO remove, this is only for testing + 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 => new Permission(claim, prefix)) + .ToList(); + } } @@ -63,5 +95,20 @@ namespace zero.Core.Api /// Find user by email /// Task GetUserByEmail(string email); + + /// + /// 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); } } diff --git a/zero.Core/Constants.cs b/zero.Core/Constants.cs index 2496a22a..870c7db8 100644 --- a/zero.Core/Constants.cs +++ b/zero.Core/Constants.cs @@ -15,9 +15,9 @@ public const string IsSuper = "zero.claim.issuper"; public const string UserId = "zero.claim.userid"; public const string UserName = "zero.claim.username"; - public const string RoleId = "zero.claim.roleid"; + public const string Role = "zero.claim.rolealias"; public const string SecurityStamp = "zero.claim.securitystamp"; - public const string Permissions = "zero.claim.permissions"; + public const string Permission = "zero.claim.permission"; } } diff --git a/zero.Core/Entities/User/User.cs b/zero.Core/Entities/User/User.cs index ed2636ac..710b1862 100644 --- a/zero.Core/Entities/User/User.cs +++ b/zero.Core/Entities/User/User.cs @@ -30,7 +30,7 @@ namespace zero.Core.Entities /// - public List RoleIds { get; set; } = new List(); + public List Roles { get; set; } = new List(); /// public List Claims { get; set; } = new List(); @@ -109,9 +109,9 @@ namespace zero.Core.Entities /// - /// The roles of the user + /// The roles (aliases) of the user /// - List RoleIds { get; set; } + List Roles { get; set; } /// /// The user's claims, for use in claims-based authentication. diff --git a/zero.Core/Entities/User/UserClaim.cs b/zero.Core/Entities/User/UserClaim.cs index 8eed1a95..5d2aa929 100644 --- a/zero.Core/Entities/User/UserClaim.cs +++ b/zero.Core/Entities/User/UserClaim.cs @@ -17,6 +17,18 @@ namespace zero.Core.Entities public UserClaim() { } + public UserClaim(string type, string value) + { + Type = type; + Value = value; + } + + public UserClaim(string type, string key, string value) + { + Type = type; + Value = key + ":" + value; + } + public UserClaim(Claim claim) { Type = claim?.Type; diff --git a/zero.Core/Entities/User/UserRole.cs b/zero.Core/Entities/User/UserRole.cs index b46dbeab..263fd194 100644 --- a/zero.Core/Entities/User/UserRole.cs +++ b/zero.Core/Entities/User/UserRole.cs @@ -4,6 +4,9 @@ namespace zero.Core.Entities { public class UserRole : DatabaseEntity, IUserRole { + /// + public string Description { get; set; } + /// public string Icon { get; set; } @@ -14,6 +17,11 @@ namespace zero.Core.Entities public interface IUserRole : IDatabaseEntity { + /// + /// Additional description + /// + string Description { get; set; } + /// /// Displayed icon alongside name /// diff --git a/zero.Core/Identity/Permission.cs b/zero.Core/Identity/Permission.cs new file mode 100644 index 00000000..80c4bc79 --- /dev/null +++ b/zero.Core/Identity/Permission.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using zero.Core.Extensions; + +namespace zero.Core.Identity +{ + public class Permission + { + public string Key { get; set; } + + public string NormalizedKey { get; set; } + + public string Value { get; set; } + + public bool CanRead => Value == PermissionsValue.Read || Value == PermissionsValue.Write; + + public bool CanWrite => Value == PermissionsValue.Write; + + public bool IsTrue => Value == PermissionsValue.True; + + public bool IsFalse => Value == PermissionsValue.False; + + + public Permission() { } + + public Permission(Claim claim, string prefixToRemove = null) : this(claim.Value, prefixToRemove) { } + + public Permission(string claimValue, string prefixToRemove = null) + { + string[] valueParts = claimValue.Split(':'); + + Key = valueParts[0]; + NormalizedKey = Key; + Value = valueParts.Length > 1 ? valueParts[1] : null; + + if (!prefixToRemove.IsNullOrEmpty()) + { + NormalizedKey = valueParts[0].TrimStart(prefixToRemove); + } + } + + + public static bool CanReadKey(IEnumerable permissions, string key, bool isNormalized = false) + { + Permission permission = permissions.FirstOrDefault(p => isNormalized ? p.NormalizedKey == key : p.Key == key); + + if (permission == null) + { + return false; + } + + return permission.CanRead; + } + + + public static bool CanWriteKey(IEnumerable permissions, string key, bool isNormalized = false) + { + Permission permission = permissions.FirstOrDefault(p => isNormalized ? p.NormalizedKey == key : p.Key == key); + + if (permission == null) + { + return false; + } + + return permission.CanWrite; + } + } +} diff --git a/zero.Core/Identity/Permissions.cs b/zero.Core/Identity/Permissions.cs index 58114043..0f0f5ba9 100644 --- a/zero.Core/Identity/Permissions.cs +++ b/zero.Core/Identity/Permissions.cs @@ -2,23 +2,33 @@ { public struct Permissions { + /// + /// Ability to switch zero applications and read/write other applications out of the user appId + /// If PermissionsValue.Write is set for this permission, the user will be limited to his/her claims in other applications too + /// + public const string Applications = "applications"; + + public struct Settings { - public const string Updates = "settings.area." + Constants.SettingsAreas.Updates; - public const string Applications = "settings.area." + Constants.SettingsAreas.Applications; - public const string Users = "settings.area." + Constants.SettingsAreas.Users; - public const string Translations = "settings.area." + Constants.SettingsAreas.Translations; - public const string Countries = "settings.area." + Constants.SettingsAreas.Countries; - public const string Logging = "settings.area." + Constants.SettingsAreas.Logging; + public const string PREFIX = "settings.area."; + public const string Updates = PREFIX + Constants.SettingsAreas.Updates; + public const string Applications = PREFIX + Constants.SettingsAreas.Applications; + public const string Users = PREFIX + Constants.SettingsAreas.Users; + public const string Translations = PREFIX + Constants.SettingsAreas.Translations; + public const string Countries = PREFIX + Constants.SettingsAreas.Countries; + public const string Logging = PREFIX + Constants.SettingsAreas.Logging; } + public struct Sections { - public const string Dashboard = "section." + Constants.Sections.Dashboard; - public const string Pages = "section." + Constants.Sections.Pages; - public const string Lists = "section." + Constants.Sections.Lists; - public const string Media = "section." + Constants.Sections.Media; - public const string Settings = "section." + Constants.Sections.Settings; + public const string PREFIX = "section."; + public const string Dashboard = PREFIX + Constants.Sections.Dashboard; + public const string Pages = PREFIX + Constants.Sections.Pages; + public const string Lists = PREFIX + Constants.Sections.Lists; + public const string Media = PREFIX + Constants.Sections.Media; + public const string Settings = PREFIX + Constants.Sections.Settings; } } diff --git a/zero.Core/Identity/RoleStore.cs b/zero.Core/Identity/RoleStore.cs index 6559c7f8..11c1df16 100644 --- a/zero.Core/Identity/RoleStore.cs +++ b/zero.Core/Identity/RoleStore.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; +using zero.Core.Api; using zero.Core.Entities; using zero.Core.Extensions; @@ -85,14 +86,14 @@ namespace zero.Core.Identity /// public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) { - return Task.FromResult(role.Name); + return Task.FromResult(role.Alias); } /// public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) { - role.Name = roleName; + role.Alias = Alias.Generate(roleName); return Task.CompletedTask; } @@ -100,7 +101,7 @@ namespace zero.Core.Identity /// public Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) { - return Task.FromResult(role.Name); + return Task.FromResult(role.Alias); } @@ -126,7 +127,7 @@ namespace zero.Core.Identity { using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) { - return await session.Query().FirstOrDefaultAsync(x => x.Name == normalizedRoleName, cancellationToken); + return await session.Query().FirstOrDefaultAsync(x => x.Alias == normalizedRoleName, cancellationToken); } } diff --git a/zero.Core/Identity/UserStore.Role.cs b/zero.Core/Identity/UserStore.Role.cs index 02ec878d..8e675d0e 100644 --- a/zero.Core/Identity/UserStore.Role.cs +++ b/zero.Core/Identity/UserStore.Role.cs @@ -16,7 +16,7 @@ namespace zero.Core.Identity /// public Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - user.RoleIds.Add(roleName); + user.Roles.Add(roleName); return Task.CompletedTask; } @@ -24,7 +24,7 @@ namespace zero.Core.Identity /// public Task> GetRolesAsync(TUser user, CancellationToken cancellationToken) { - return Task.FromResult((IList)user.RoleIds.ToList()); + return Task.FromResult((IList)user.Roles.ToList()); } @@ -33,7 +33,7 @@ namespace zero.Core.Identity { using (IAsyncDocumentSession session = Raven.OpenAsyncSession()) { - return await session.Query().Where(x => roleName.In(x.RoleIds)).ToListAsync(); + return await session.Query().Where(x => roleName.In(x.Roles)).ToListAsync(); } } @@ -41,14 +41,14 @@ namespace zero.Core.Identity /// public Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - return Task.FromResult(user.RoleIds.Contains(roleName, StringComparer.InvariantCultureIgnoreCase)); + return Task.FromResult(user.Roles.Contains(roleName, StringComparer.InvariantCultureIgnoreCase)); } /// public Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken) { - user.RoleIds.Remove(roleName); + user.Roles.Remove(roleName); return Task.CompletedTask; } } diff --git a/zero.Core/Identity/ZeroAuthorizeAttribute.cs b/zero.Core/Identity/ZeroAuthorizeAttribute.cs index ead26e85..02a51338 100644 --- a/zero.Core/Identity/ZeroAuthorizeAttribute.cs +++ b/zero.Core/Identity/ZeroAuthorizeAttribute.cs @@ -65,18 +65,18 @@ namespace zero.Core.Identity // check claims if (!Permission.IsNullOrEmpty()) { - bool isSuperUser = user.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); + bool isSuperUser = false; // TODO user.HasClaim(Constants.Auth.Claims.IsSuper, PermissionsValue.True); bool hasPassed = isSuperUser; if (!isSuperUser) { foreach (string value in PermissionValues) { - bool fulfillsClaim = user.HasClaim(Constants.Auth.Claims.Permissions, Permission + ":" + value); + bool fulfillsClaim = user.HasClaim(Constants.Auth.Claims.Permission, Permission + ":" + value); if (!fulfillsClaim && value == PermissionsValue.Read) { - fulfillsClaim = user.HasClaim(Constants.Auth.Claims.Permissions, Permission + ":" + PermissionsValue.Write); + fulfillsClaim = user.HasClaim(Constants.Auth.Claims.Permission, Permission + ":" + PermissionsValue.Write); } if (fulfillsClaim) diff --git a/zero.Core/Validation/BackofficeUserValidator.cs b/zero.Core/Validation/BackofficeUserValidator.cs index 8a891560..d468e303 100644 --- a/zero.Core/Validation/BackofficeUserValidator.cs +++ b/zero.Core/Validation/BackofficeUserValidator.cs @@ -12,7 +12,7 @@ namespace zero.Core.Validation RuleFor(x => x.Email).Email(); RuleFor(x => x.PasswordHash).NotEmpty(); RuleFor(x => x.LanguageId).NotEmpty(); // TODO only allow available languages - RuleFor(x => x.RoleIds).NotEmpty(); + RuleFor(x => x.Roles).NotEmpty(); if (isCreate) { diff --git a/zero.Web/App/pages/pages/routes.js b/zero.Web/App/pages/pages/routes.js index 435e295a..8bdbfdf4 100644 --- a/zero.Web/App/pages/pages/routes.js +++ b/zero.Web/App/pages/pages/routes.js @@ -1,29 +1,38 @@ -const alias = zero.alias.sections.pages; +import { find as _find } from 'underscore'; + +const alias = zero.alias.sections.pages; +const section = _find(zero.sections, section => section.alias === alias); +let routes = []; + +if (section) +{ + routes.push({ + path: 'edit/:id', + props: true, + name: 'page', + component: () => import('zero/pages/' + alias + '/page') + }); + + routes.push({ + path: 'recyclebin', + name: 'recyclebin', + component: () => import('zero/pages/' + alias + '/recyclebin'), + meta: { + name: '@page.recyclebin.name' + } + }); + + routes.push({ + path: 'history', + name: 'history', + component: () => import('zero/pages/' + alias + '/history'), + meta: { + name: '@page.history.name' + } + }); +} export default { section: alias, - routes: [ - { - path: 'edit/:id', - props: true, - name: 'page', - component: () => import('zero/pages/' + alias + '/page') - }, - { - path: 'recyclebin', - name: 'recyclebin', - component: () => import('zero/pages/' + alias + '/recyclebin'), - meta: { - name: '@page.recyclebin.name' - } - }, - { - path: 'history', - name: 'history', - component: () => import('zero/pages/' + alias + '/history'), - meta: { - name: '@page.history.name' - } - } - ] + routes }; \ No newline at end of file diff --git a/zero.Web/App/pages/settings/routes.js b/zero.Web/App/pages/settings/routes.js index 22361163..ee9547a8 100644 --- a/zero.Web/App/pages/settings/routes.js +++ b/zero.Web/App/pages/settings/routes.js @@ -2,18 +2,21 @@ const alias = zero.alias.sections.settings; const section = _find(zero.sections, section => section.alias === alias); -const areas = zero.settingsAreas; +let routes = []; -export default { - routes: _map(areas, area => +if (section) +{ + zero.settingsAreas.forEach(group => group.items.forEach(area => { - return { + routes.push({ path: area.url, name: alias + '-' + area.alias, component: () => import(`zero/pages/${alias}/${area.alias}`), meta: { - name: [area.name, section.name ] + name: [area.name, section.name] } - }; - }) -}; \ No newline at end of file + }); + })); +} + +export default { routes }; \ No newline at end of file diff --git a/zero.Web/App/pages/settings/settings.vue b/zero.Web/App/pages/settings/settings.vue index cbea87ec..18cccefa 100644 --- a/zero.Web/App/pages/settings/settings.vue +++ b/zero.Web/App/pages/settings/settings.vue @@ -28,34 +28,12 @@ data: () => ({ page: true, - groups: [], + groups: zero.settingsAreas, tokens: { 'zero_version': '1.0.0-alpha.1', 'plugin_count': 7 } - }), - - created() - { - SettingsApi.getAreas().then(groups => - { - groups.forEach(group => group.items.forEach(item => - { - item.url = '/' + zero.alias.sections.settings + '/' + item.alias; - })); - this.groups = groups; - }); - }, - - - methods: { - - onBack() - { - console.info('back'); - } - - } + }) } diff --git a/zero.Web/App/router.config.js b/zero.Web/App/router.config.js index af168fa9..633e1888 100644 --- a/zero.Web/App/router.config.js +++ b/zero.Web/App/router.config.js @@ -103,7 +103,7 @@ addRoutesPerContext(require.context('@/Plugins', true, /routes\.js$/), true); // routes.push({ name: '404', path: '*', component: () => import('zero/pages/notfound') }); - +console.info(routes); // create the router with history mode diff --git a/zero.Web/Controllers/SettingsController.cs b/zero.Web/Controllers/SettingsController.cs index d9f93fa5..ae58a856 100644 --- a/zero.Web/Controllers/SettingsController.cs +++ b/zero.Web/Controllers/SettingsController.cs @@ -1,10 +1,13 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; +using System.Collections.Generic; using zero.Core; using zero.Core.Api; +using zero.Core.Entities; +using zero.Core.Identity; namespace zero.Web.Controllers { + [ZeroAuthorize(Permissions.Sections.Settings, PermissionsValue.Read)] public class SettingsController : BackofficeController { private ISettingsApi Api { get; set; } @@ -13,11 +16,5 @@ namespace zero.Web.Controllers { Api = api; } - - - public IActionResult GetAreas() - { - return Json(Api.GetAreas()); - } } } diff --git a/zero.Web/Controllers/TestController.cs b/zero.Web/Controllers/TestController.cs index f62f0098..4f49ce7f 100644 --- a/zero.Web/Controllers/TestController.cs +++ b/zero.Web/Controllers/TestController.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using System.Linq; using System.Threading.Tasks; using zero.Core; using zero.Core.Api; @@ -48,6 +49,13 @@ namespace zero.Web.Controllers } + [ZeroAuthorize] + public IActionResult GetUserClaims() + { + return Json(HttpContext.User.Claims.Select(claim => new { claim.Type, claim.Value }).ToArray()); + } + + [HttpPost] public async Task Logout() { diff --git a/zero.Web/Setup/setup.vue b/zero.Web/Setup/setup.vue index b3256ae3..aad27bbf 100644 --- a/zero.Web/Setup/setup.vue +++ b/zero.Web/Setup/setup.vue @@ -29,7 +29,7 @@ data: () => ({ step: 0, steps: ['step-user', 'step-database', 'step-application', 'step-install', 'step-finish'], - model: { "appName": "Brothers", "user": { "name": "Tobi", "email": "tobi@brothers.studio", "password": "tobi1TOBI!" }, "database": { "url": "http://localhost:9800", "name": "zero" } } + model: { "appName": "Brothers", "user": { "name": "Tobias Klika", "email": "", "password": "" }, "database": { "url": "http://localhost:9800", "name": "zero" } } //model: { // appName: null, // user: { diff --git a/zero.Web/ZeroBuilder.cs b/zero.Web/ZeroBuilder.cs index a98080fe..0645e53d 100644 --- a/zero.Web/ZeroBuilder.cs +++ b/zero.Web/ZeroBuilder.cs @@ -27,7 +27,7 @@ namespace zero.Web { opts.ClaimsIdentity.UserIdClaimType = Constants.Auth.Claims.UserId; opts.ClaimsIdentity.UserNameClaimType = Constants.Auth.Claims.UserName; - opts.ClaimsIdentity.RoleClaimType = Constants.Auth.Claims.RoleId; + opts.ClaimsIdentity.RoleClaimType = Constants.Auth.Claims.Role; opts.ClaimsIdentity.SecurityStampClaimType = Constants.Auth.Claims.SecurityStamp; opts.Password.RequireDigit = false; diff --git a/zero.Web/ZeroVue.cs b/zero.Web/ZeroVue.cs index 35132514..88ca4a87 100644 --- a/zero.Web/ZeroVue.cs +++ b/zero.Web/ZeroVue.cs @@ -11,6 +11,7 @@ using zero.Core; using zero.Core.Api; using zero.Core.Entities; using zero.Core.Extensions; +using zero.Core.Identity; using zero.Web.Sections; namespace zero.Web @@ -69,10 +70,18 @@ namespace zero.Web /// IList CreateSections() { + bool isSuperUser = UserApi.IsSuper(); + IList permissions = UserApi.GetPermissions(Permissions.Sections.PREFIX); + List sections = new List(); foreach (ISection section in Options.Sections) { + if (!isSuperUser && !Permission.CanReadKey(permissions, section.Alias, true)) + { + continue; + } + bool isExternal = !(section is IBuiltInSection); string url = Alias.Generate(section.Alias).EnsureStartsWith('/'); @@ -133,27 +142,47 @@ namespace zero.Web /// /// Creates the areas in the settings section /// - IList CreateSettingsAreas() + IList CreateSettingsAreas() { - List areas = new List(); + bool isSuperUser = UserApi.IsSuper(); + IList permissions = UserApi.GetPermissions(Permissions.Settings.PREFIX); + + List groups = new List(); foreach (SettingsGroup group in Options.SettingsAreas) { + List areas = new List(); + foreach (SettingsArea area in group.Items) { + if (!isSuperUser && !Permission.CanReadKey(permissions, area.Alias, true)) + { + continue; + } + ZeroVueSettingsArea vueArea = new ZeroVueSettingsArea() { Alias = area.Alias, Name = area.Name, + Description = area.Description, Icon = area.Icon, Url = Constants.Sections.Settings.EnsureStartsWith('/') + Alias.Generate(area.Alias).EnsureStartsWith('/') }; areas.Add(vueArea); } + + if (areas.Count > 0) + { + groups.Add(new ZeroVueSettingsGroup() + { + Name = group.Name, + Items = areas + }); + } } - return areas; + return groups; } @@ -217,7 +246,7 @@ namespace zero.Web public IList Applications { get; set; } = new List(); - public IList SettingsAreas { get; set; } = new List(); + public IList SettingsAreas { get; set; } = new List(); public Dictionary> Alias { get; set; } = new Dictionary>(); @@ -243,12 +272,22 @@ namespace zero.Web } + public class ZeroVueSettingsGroup + { + public string Name { get; set; } + + public IList Items { get; set; } + } + + public class ZeroVueSettingsArea { public string Alias { get; set; } public string Name { get; set; } + public string Description { get; set; } + public string Icon { get; set; } public string Url { get; set; }