From 3caf0bd766bb9cb45c382d1f07fb67e582050204 Mon Sep 17 00:00:00 2001 From: James Kolpack Date: Tue, 27 Sep 2016 11:56:10 -0400 Subject: [PATCH] Add administrator editing --- .../Controllers/UserController.cs | 56 +++++++++++++--- InventoryTraker.Web/Global.asax.cs | 2 + .../Helpers/AngularModelHelper.cs | 66 +++++++++++++------ .../Identity/ApplicationUserManager.cs | 10 +++ InventoryTraker.Web/Migrations/SeedData.cs | 28 ++++++++ InventoryTraker.Web/Models/UserEditForm.cs | 3 + InventoryTraker.Web/Models/UserViewModel.cs | 2 + .../Views/Shared/_Layout.cshtml | 52 ++++++++------- .../Views/Shared/_Navigation.cshtml | 14 ++-- .../Views/Shared/_NavigationNoAuth.cshtml | 2 +- InventoryTraker.Web/css/layout.css | 22 +++++++ .../js/user/templates/userList.tmpl.cshtml | 5 ++ InventoryTraker.Web/js/user/userSvc.js | 4 +- 13 files changed, 207 insertions(+), 59 deletions(-) diff --git a/InventoryTraker.Web/Controllers/UserController.cs b/InventoryTraker.Web/Controllers/UserController.cs index 2e310bd..d55e341 100644 --- a/InventoryTraker.Web/Controllers/UserController.cs +++ b/InventoryTraker.Web/Controllers/UserController.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using System.Web.Mvc; using AutoMapper; -using AutoMapper.QueryableExtensions; using InventoryTraker.Web.Attributes; using InventoryTraker.Web.Core; using InventoryTraker.Web.Identity; @@ -11,9 +10,10 @@ using Microsoft.AspNet.Identity; namespace InventoryTraker.Web.Controllers { + [Authorize(Roles = ApplicationRoleManager.AdminRoleName)] public class UserController : ControllerBase { - private readonly ApplicationUserManager _userManager; + private readonly ApplicationUserManager _userManager; private readonly IMapper _mapper; public UserController(ApplicationUserManager userManager, IMapper mapper) @@ -29,13 +29,18 @@ namespace InventoryTraker.Web.Controllers public JsonResult All() { - var users = - _userManager - .Users - .ProjectTo(_mapper.ConfigurationProvider) - .OrderBy(u => u.UserName); + var users = + from u in _userManager.Users.ToList() + let ad = _userManager.GetRoles(u.Id).Contains(ApplicationRoleManager.AdminRoleName) + orderby u.UserName + select new UserViewModel + { + UserName = u.UserName, + Email = u.Email, + Administrator = ad + }; - return BetterJson(users); + return BetterJson(users.ToList()); } [ActionLog] @@ -57,7 +62,17 @@ namespace InventoryTraker.Web.Controllers if (!identityResult.Succeeded) return GetErrorListJson(identityResult.Errors.ToArray()); - return BetterJson(_mapper.Map(user)); + user = _userManager.FindByEmail(form.Email); + if (form.Administrator) + { + var result = _userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName); + if (!result.Succeeded) + return GetErrorListJson(result.Errors.ToArray()); + } + + var userViewModel = _mapper.Map(user); + userViewModel.Administrator = _userManager.IsInRole(user.Id, ApplicationRoleManager.AdminRoleName); + return BetterJson(userViewModel); } [ActionLog] @@ -79,12 +94,33 @@ namespace InventoryTraker.Web.Controllers return GetErrorListJson(resetResult.Errors.ToArray()); } + var rolesForUser = _userManager.GetRoles(user.Id); + + if (rolesForUser.Contains(ApplicationRoleManager.AdminRoleName) && !form.Administrator) + { + var currentUser = _userManager.FindById(User.Identity.GetUserId()); + if (currentUser == user) + return GetErrorListJson("Cannot remove admin from yourself"); + + var result = _userManager.RemoveFromRole(user.Id, ApplicationRoleManager.AdminRoleName); + if (!result.Succeeded) + return GetErrorListJson(result.Errors.ToArray()); + } + else if (!rolesForUser.Contains(ApplicationRoleManager.AdminRoleName) && form.Administrator) + { + var result = _userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName); + if (!result.Succeeded) + return GetErrorListJson(result.Errors.ToArray()); + } + var identityResult = _userManager.Update(user); if (!identityResult.Succeeded) return GetErrorListJson(identityResult.Errors.ToArray()); - return BetterJson(_mapper.Map(user)); + var userViewModel = _mapper.Map(user); + userViewModel.Administrator = _userManager.IsInRole(user.Id, ApplicationRoleManager.AdminRoleName); + return BetterJson(userViewModel); } } } \ No newline at end of file diff --git a/InventoryTraker.Web/Global.asax.cs b/InventoryTraker.Web/Global.asax.cs index 8a110a4..b51b0f8 100644 --- a/InventoryTraker.Web/Global.asax.cs +++ b/InventoryTraker.Web/Global.asax.cs @@ -2,6 +2,7 @@ using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; +using InventoryTraker.Web.Migrations; namespace InventoryTraker.Web { @@ -14,6 +15,7 @@ namespace InventoryTraker.Web BundleConfig.RegisterBundles(BundleTable.Bundles); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); EFConfig.Initialize(); + SeedData.AddAdminRole(); //SeedData.Init(); } } diff --git a/InventoryTraker.Web/Helpers/AngularModelHelper.cs b/InventoryTraker.Web/Helpers/AngularModelHelper.cs index 7a86f97..e30b8f5 100644 --- a/InventoryTraker.Web/Helpers/AngularModelHelper.cs +++ b/InventoryTraker.Web/Helpers/AngularModelHelper.cs @@ -75,40 +75,68 @@ namespace InventoryTraker.Web.Helpers var expression = ExpressionForInternal(property); - //Creates
- var formGroup = new HtmlTag("div") - .AddClasses("form-group", "has-feedback") - .Attr("form-group-validation", name); - var labelText = metadata.DisplayName ?? name.Humanize(LetterCasing.Title); //Creates var label = new HtmlTag("label") - .AddClass("control-label") .Attr("for", name) .Text(labelText); - var tagName = metadata.DataTypeName == "MultilineText" - ? "textarea" + var tagName = + metadata.DataTypeName == "MultilineText" + ? "textarea" : "input"; - var placeholder = metadata.Watermark ?? - (labelText + "..."); //Creates var input = new HtmlTag(tagName) - .AddClass("form-control") .Attr("ng-model", expression) - .Attr("name", name) - .Attr("type", "text") - .Attr("placeholder", placeholder); + .Attr("name", name); - ApplyValidationToInput(input, metadata); + var formGroup = new HtmlTag("div"); - return formGroup - .Append(label) - .Append(input); + if (metadata.ModelType != typeof(bool)) + { + label.AddClass("control-label"); + + var placeholder = metadata.Watermark ?? + labelText + "..."; + + input + .AddClass("form-control") + .Attr("type", "text") + .Attr("placeholder", placeholder); + + ApplyValidationToInput(input, metadata); + + //Creates
+ formGroup + .AddClass("form-group") + .AddClass("has-feedback") + .Attr("form-group-validation", name) + .Append(label) + .Append(input); + } + else if (metadata.ModelType == typeof(bool)) + { + label.AddClass("form-check-label"); + + input + .AddClass("form-check-input") + .Attr("type", "checkbox"); + + label.Text("") + .Append(input) + .AppendHtml("  ") + .Append(new HtmlTag("text").NoTag().Text(labelText)); + + formGroup + .AddClass("form-check") + .Append(label); + } + + return formGroup; } private void ApplyValidationToInput(HtmlTag input, ModelMetadata metadata) diff --git a/InventoryTraker.Web/Identity/ApplicationUserManager.cs b/InventoryTraker.Web/Identity/ApplicationUserManager.cs index c8eb325..ab047be 100644 --- a/InventoryTraker.Web/Identity/ApplicationUserManager.cs +++ b/InventoryTraker.Web/Identity/ApplicationUserManager.cs @@ -2,11 +2,21 @@ using System; using System.Threading.Tasks; using InventoryTraker.Web.Core; using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin.Security.DataProtection; namespace InventoryTraker.Web.Identity { + public class ApplicationRoleManager : RoleManager + { + public const string AdminRoleName = "Admin"; + + public ApplicationRoleManager(IRoleStore store) : base(store) + { + } + } + public class ApplicationUserManager : UserManager { public ApplicationUserManager(IUserStore store, IDataProtectionProvider dataProtectionProvider) diff --git a/InventoryTraker.Web/Migrations/SeedData.cs b/InventoryTraker.Web/Migrations/SeedData.cs index 45a9a4b..c120034 100644 --- a/InventoryTraker.Web/Migrations/SeedData.cs +++ b/InventoryTraker.Web/Migrations/SeedData.cs @@ -14,6 +14,34 @@ namespace InventoryTraker.Web.Migrations { public static class SeedData { + + public static void AddAdminRole() + { + using (var context = new AppDbContext()) + AddAdminRole(context); + } + + private static void AddAdminRole(AppDbContext context) + { + var manager = new ApplicationRoleManager(new RoleStore(context)); + if (!manager.RoleExists(ApplicationRoleManager.AdminRoleName)) + { + var result = manager.Create(new IdentityRole(ApplicationRoleManager.AdminRoleName)); + } + + // if no users are admins, make them all! + var adminRole = manager.Roles.First(r => r.Name == ApplicationRoleManager.AdminRoleName); + var userManager = new ApplicationUserManager(new UserStore(context), null); + var admins = userManager.Users.Where(u => u.Roles.Any(r => r.RoleId == adminRole.Id)); + if (!admins.Any()) + { + foreach (var user in userManager.Users.ToList()) + { + userManager.AddToRole(user.Id, ApplicationRoleManager.AdminRoleName); + } + } + } + public static void Init() { using (var context = new AppDbContext()) diff --git a/InventoryTraker.Web/Models/UserEditForm.cs b/InventoryTraker.Web/Models/UserEditForm.cs index 73e3223..1939b15 100644 --- a/InventoryTraker.Web/Models/UserEditForm.cs +++ b/InventoryTraker.Web/Models/UserEditForm.cs @@ -14,6 +14,9 @@ namespace InventoryTraker.Web.Models [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] public string Email { get; set; } + //[Required] + public bool Administrator { get; set; } + [DataType(DataType.Password)] public string Password { get; set; } diff --git a/InventoryTraker.Web/Models/UserViewModel.cs b/InventoryTraker.Web/Models/UserViewModel.cs index 0188446..8ba56ae 100644 --- a/InventoryTraker.Web/Models/UserViewModel.cs +++ b/InventoryTraker.Web/Models/UserViewModel.cs @@ -16,6 +16,8 @@ namespace InventoryTraker.Web.Models [RegularExpression(@"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}", ErrorMessage = "Must be an email address")] public string Email { get; set; } + public bool Administrator { get; set; } + public override string ToString() { return $"UserName: {UserName}, email: {Email}"; diff --git a/InventoryTraker.Web/Views/Shared/_Layout.cshtml b/InventoryTraker.Web/Views/Shared/_Layout.cshtml index 7d6cd20..fb2dbcc 100644 --- a/InventoryTraker.Web/Views/Shared/_Layout.cshtml +++ b/InventoryTraker.Web/Views/Shared/_Layout.cshtml @@ -2,32 +2,40 @@ - - - - @ViewBag.Title - InventoryTraker - @Styles.Render("~/Content/all-styles") - @RenderSection("Styles", required: false) - - - + + + + @ViewBag.Title - Inventory Traker + @Styles.Render("~/Content/all-styles") + @RenderSection("Styles", false) + + + -
- @(Request.IsAuthenticated ? Html.Partial("_Navigation") : Html.Partial("_NavigationNoAuth")) -
-
- @RenderBody() -
+
+ @(Request.IsAuthenticated ? Html.Partial("_Navigation") : Html.Partial("_NavigationNoAuth")) +
+
+ @RenderBody()
-
- - @Scripts.Render("~/js/all-javascript") - @RenderSection("Scripts", required: false) + @if (Request.IsAuthenticated) + { +
+
+

Inventory Traker © 2016 Kolpack Software Consulting LLC

+
+
+ } + +
+ +@Scripts.Render("~/js/all-javascript") +@RenderSection("Scripts", false) diff --git a/InventoryTraker.Web/Views/Shared/_Navigation.cshtml b/InventoryTraker.Web/Views/Shared/_Navigation.cshtml index 35f4fd5..cca34ae 100644 --- a/InventoryTraker.Web/Views/Shared/_Navigation.cshtml +++ b/InventoryTraker.Web/Views/Shared/_Navigation.cshtml @@ -1,4 +1,5 @@ -