From 41b4fb300cfb6e41504f3d935121bd83e4b3db9d Mon Sep 17 00:00:00 2001 From: Jason Francis <134454748+JasonRobertFrancis@users.noreply.github.com> Date: Wed, 24 Jan 2024 09:50:18 -0800 Subject: [PATCH 01/69] Adds first-draft of directory --- .../Controllers/DirectoryController.cs | 131 +++++++++++++++ .../Models/IndividualSearchResult.cs | 75 +++++++++ web/Areas/Directory/Views/Index.cshtml | 151 ++++++++++++++++++ web/Areas/Directory/Views/UserInfo.cshtml | 5 + 4 files changed, 362 insertions(+) create mode 100644 web/Areas/Directory/Controllers/DirectoryController.cs create mode 100644 web/Areas/Directory/Models/IndividualSearchResult.cs create mode 100644 web/Areas/Directory/Views/Index.cshtml create mode 100644 web/Areas/Directory/Views/UserInfo.cshtml diff --git a/web/Areas/Directory/Controllers/DirectoryController.cs b/web/Areas/Directory/Controllers/DirectoryController.cs new file mode 100644 index 0000000..a39601d --- /dev/null +++ b/web/Areas/Directory/Controllers/DirectoryController.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Viper.Models.AAUD; +using Viper.Models.RAPS; +using Viper.Areas.RAPS.Services; +using Web.Authorization; +using Microsoft.IdentityModel.Tokens; +using Viper.Classes; +using Polly; +using Viper.Classes.SQLContext; +using Viper.Areas.RAPS.Models; +using Viper.Areas.Directory.Models; +using System; + +namespace Viper.Areas.Directory.Controllers +{ + //TODO: Create the Directory Delegate Users role and add anyone with access to delegate roles + [Area("Directory")] + [Authorize(Roles = "VMDO SVM-IT,RAPS Delegate Users", Policy = "2faAuthentication")] + public class DirectoryController : AreaController + { + private readonly Classes.SQLContext.RAPSContext _RAPSContext; + private RAPSSecurityService _securityService; + public Classes.SQLContext.AAUDContext _aaud; + public IUserHelper UserHelper; + + public DirectoryController(Classes.SQLContext.RAPSContext context) + { + _RAPSContext = context; + _securityService = new RAPSSecurityService(context); + _aaud = new AAUDContext(); + } + + /// + /// Directory home page + /// + [Route("/[area]/")] + public async Task Index() + { + return await Task.Run(() => View("~/Areas/Directory/Views/Index.cshtml")); + } + + [Route("/[area]/[action]")] + public async Task>> Nav(int? roleId, int? permissionId, string? memberId, string instance = "VIPER") + { + var nav = new List(); + return nav; + } + + /// + /// Directory list + /// + /// search string + /// + [Route("/[area]/search/{search}")] + public async Task>> Get(string search) + { + var individuals = await _aaud.AaudUsers + .Where(u => (u.DisplayFirstName + " " + u.DisplayLastName).Contains(search) + || (u.MailId != null && u.MailId.Contains(search)) + || (u.LoginId != null && u.LoginId.Contains(search)) + ) + .Where(u => u.Current != 0) + .OrderBy(u => u.DisplayLastName) + .ThenBy(u => u.DisplayFirstName) + .ToListAsync(); + List results = new(); + individuals.ForEach(m => + { + results.Add(new IndividualSearchResult() + { + ClientId = m.ClientId, + MothraId = m.MothraId, + LoginId = m.LoginId, + MailId = m.MailId, + SpridenId = m.SpridenId, + Pidm = m.Pidm, + EmployeeId = m.EmployeeId, + VmacsId = m.VmacsId, + VmcasId = m.VmcasId, + UnexId = m.UnexId, + MivId = m.MivId, + LastName = m.LastName, + FirstName = m.FirstName, + MiddleName = m.MiddleName, + DisplayLastName = m.DisplayLastName, + DisplayFirstName = m.DisplayFirstName, + DisplayMiddleName = m.DisplayMiddleName, + DisplayFullName = m.DisplayFullName, + Name = m.DisplayFullName, + CurrentStudent = m.CurrentStudent, + FutureStudent = m.FutureStudent, + CurrentEmployee = m.CurrentEmployee, + FutureEmployee = m.FutureEmployee, + StudentTerm = m.StudentTerm, + EmployeeTerm = m.EmployeeTerm, + PpsId = m.PpsId, + StudentPKey = m.StudentPKey, + EmployeePKey = m.EmployeePKey, + Current = m.Current, + Future = m.Future, + IamId = m.IamId, + Ross = m.Ross, + Added = m.Added + }); + }); + results.ForEach(r => + { + LdapUser l = new LdapService().GetUser(r.LoginId); + r.Title = l.Title; + r.Department = l.Department; + }); + + return results; + } + + + /// + /// Directory results + /// + /// User ID + /// + [Route("/[area]/userInfo/{mothraID}")] + public async Task DirectoryResult(string mothraID) + { + // pull in the user based on uid + return await Task.Run(() => View("~/Areas/Directory/Views/UserInfo.cshtml")); + } + } +} diff --git a/web/Areas/Directory/Models/IndividualSearchResult.cs b/web/Areas/Directory/Models/IndividualSearchResult.cs new file mode 100644 index 0000000..7f47826 --- /dev/null +++ b/web/Areas/Directory/Models/IndividualSearchResult.cs @@ -0,0 +1,75 @@ +namespace Viper.Areas.Directory.Models +{ + public class IndividualSearchResult + { + public string ClientId { get; set; } = string.Empty; + + public string MothraId { get; set; } = string.Empty; + + public string? LoginId { get; set; } = string.Empty; + + public string? MailId { get; set; } = string.Empty; + + public string? SpridenId { get; set; } = string.Empty; + + public string? Pidm { get; set; } = string.Empty; + + public string? EmployeeId { get; set; } = string.Empty; + + public int? VmacsId { get; set; } = null!; + + public string? VmcasId { get; set; } = string.Empty; + + public string? UnexId { get; set; } = string.Empty; + + public int? MivId { get; set; } = null!; + + public string LastName { get; set; } = string.Empty; + + public string FirstName { get; set; } = string.Empty; + + public string? MiddleName { get; set; } = string.Empty; + + public string DisplayLastName { get; set; } = string.Empty; + + public string DisplayFirstName { get; set; } = string.Empty; + + public string? DisplayMiddleName { get; set; } = string.Empty; + + public string DisplayFullName { get; set; } = string.Empty; + public string Name { get; set; } = string.Empty; + public bool CurrentStudent { get; set; } = false; + + public bool FutureStudent { get; set; } = false; + + public bool CurrentEmployee { get; set; } = false; + + public bool FutureEmployee { get; set; } = false; + + public int? StudentTerm { get; set; } = null!; + + public int? EmployeeTerm { get; set; } = null!; + + public string? PpsId { get; set; } = string.Empty; + + public string? StudentPKey { get; set; } = string.Empty; + + public string? EmployeePKey { get; set; } = string.Empty; + public string? Title { get; set; } = string.Empty; + public string? Department { get; set; } = string.Empty; + + public int Current { get; set; } = -1; + + public int Future { get; set; } = -1; + + public string? IamId { get; set; } = string.Empty; + + public bool? Ross { get; set; } = null!; + + public DateTime? Added { get; set; } = null!; + + public bool Can_See_IDs { get; set; } = false; + public bool Can_Spoof { get; set; } = false; + public bool Can_See_UserInfo { get; set; } = false; + } +} diff --git a/web/Areas/Directory/Views/Index.cshtml b/web/Areas/Directory/Views/Index.cshtml new file mode 100644 index 0000000..79a1d04 --- /dev/null +++ b/web/Areas/Directory/Views/Index.cshtml @@ -0,0 +1,151 @@ +@using Viper.Classes.SQLContext; +@model Viper.Models.AAUD.AaudUser +@{ + RAPSContext? rapsContext = (RAPSContext?)Context.RequestServices.GetService(typeof(RAPSContext)); + IUserHelper UserHelper = new UserHelper(); +} +@{ + ViewData["Title"] = "Directory"; +} +

Directory

+ + + + + + + + + + + + + +@section Scripts { + + +} \ No newline at end of file diff --git a/web/Areas/Directory/Views/UserInfo.cshtml b/web/Areas/Directory/Views/UserInfo.cshtml new file mode 100644 index 0000000..87f162d --- /dev/null +++ b/web/Areas/Directory/Views/UserInfo.cshtml @@ -0,0 +1,5 @@ +@{ + ViewData["Title"] = "User Info Page"; +} +
+
From be69e19c758f19eb946136b93f0690930fc0648f Mon Sep 17 00:00:00 2001 From: Jason Francis <134454748+JasonRobertFrancis@users.noreply.github.com> Date: Wed, 31 Jan 2024 08:24:17 -0800 Subject: [PATCH 02/69] Addresses Brandon's first round of recommendations I'm sure there will be additional back-and-forth, but here's the next round of changes: --- [Permission(Allow = "SVMSecure")] is probably a sufficient permission check. As it is now, this restricts the page to a much smaller set of users. Done --- Make sure to remove copy/pasted variables that are not needed in this class Sorry about that; I had left in some of my early code (as I was trying to figure out how all this works!). I removed the variables, but I added back in what seemed necessary to do the permission check. --- We might need a permission check here for the IDs. Since this data is sent to the browser via AJAX, it's visible to the user, even if not included in the page. One way to handle this would be the IndividualSearchResult class only containing data visible to everyone, and then an IndividualSearchResultWithIds child class containing the restricted data. Maybe you can help me think about a better way of doing this. I couldn't quite figure out how to use a generic to be able to switch between the two possible resulting objects. --- There's a static function GetOldViperRootURL() in HttpHelper that could be used to get the url for Viper on the current server, instead of hard coding viper.vetmed. Good tip. Thanks! Its seems like vue isn't very happy with localhost, but maybe adding a local certificate will help with some of the problems I am now getting. --- The key for the storage has to be unique across all of VIPER, so consider prefixing searchField with, e.g. directory or some other prefix. Fixed. I am now using directory_search --- I know I used 3 as the min length for RAPS, but it's been suggested to me that 2 might be better. Good suggestion. Fixed. --- Consider allowing additional ID searches - pidm, spriden id, mothra id, employee id, iam id Done --- I'm glad you were able to use this class! I added phone number to the LDAP lookup. If that is getting too unwieldy, we could pull it out of RAPS and create a generic LDAPlookup class. --- .../Controllers/DirectoryController.cs | 58 ++++++++------ .../Models/IndividualSearchResult.cs | 6 +- web/Areas/Directory/Views/Index.cshtml | 40 ++++++---- web/Areas/RAPS/Models/LdapUserContact.cs | 76 +++++++++++++++++++ web/Areas/RAPS/Services/LdapService.cs | 33 +++++++- 5 files changed, 170 insertions(+), 43 deletions(-) create mode 100644 web/Areas/RAPS/Models/LdapUserContact.cs diff --git a/web/Areas/Directory/Controllers/DirectoryController.cs b/web/Areas/Directory/Controllers/DirectoryController.cs index a39601d..563de7e 100644 --- a/web/Areas/Directory/Controllers/DirectoryController.cs +++ b/web/Areas/Directory/Controllers/DirectoryController.cs @@ -12,24 +12,24 @@ using Viper.Areas.RAPS.Models; using Viper.Areas.Directory.Models; using System; +using Viper; namespace Viper.Areas.Directory.Controllers { - //TODO: Create the Directory Delegate Users role and add anyone with access to delegate roles [Area("Directory")] - [Authorize(Roles = "VMDO SVM-IT,RAPS Delegate Users", Policy = "2faAuthentication")] + [Authorize(Roles = "VMDO SVM-IT", Policy = "2faAuthentication")] + [Permission(Allow = "SVMSecure")] public class DirectoryController : AreaController { - private readonly Classes.SQLContext.RAPSContext _RAPSContext; - private RAPSSecurityService _securityService; public Classes.SQLContext.AAUDContext _aaud; + private readonly RAPSContext? _rapsContext; public IUserHelper UserHelper; public DirectoryController(Classes.SQLContext.RAPSContext context) { - _RAPSContext = context; - _securityService = new RAPSSecurityService(context); _aaud = new AAUDContext(); + this._rapsContext = (RAPSContext?)HttpHelper.HttpContext?.RequestServices.GetService(typeof(RAPSContext)); + UserHelper = new UserHelper(); } /// @@ -41,13 +41,6 @@ public async Task Index() return await Task.Run(() => View("~/Areas/Directory/Views/Index.cshtml")); } - [Route("/[area]/[action]")] - public async Task>> Nav(int? roleId, int? permissionId, string? memberId, string instance = "VIPER") - { - var nav = new List(); - return nav; - } - /// /// Directory list /// @@ -60,6 +53,11 @@ public async Task>> Get(string .Where(u => (u.DisplayFirstName + " " + u.DisplayLastName).Contains(search) || (u.MailId != null && u.MailId.Contains(search)) || (u.LoginId != null && u.LoginId.Contains(search)) + || (u.SpridenId != null && u.SpridenId.Contains(search)) + || (u.Pidm != null && u.Pidm.Contains(search)) + || (u.MothraId != null && u.MothraId.Contains(search)) + || (u.EmployeeId != null && u.EmployeeId.Contains(search)) + || (u.IamId != null && u.IamId.Contains(search)) ) .Where(u => u.Current != 0) .OrderBy(u => u.DisplayLastName) @@ -70,17 +68,9 @@ public async Task>> Get(string { results.Add(new IndividualSearchResult() { - ClientId = m.ClientId, MothraId = m.MothraId, LoginId = m.LoginId, MailId = m.MailId, - SpridenId = m.SpridenId, - Pidm = m.Pidm, - EmployeeId = m.EmployeeId, - VmacsId = m.VmacsId, - VmcasId = m.VmcasId, - UnexId = m.UnexId, - MivId = m.MivId, LastName = m.LastName, FirstName = m.FirstName, MiddleName = m.MiddleName, @@ -102,14 +92,36 @@ public async Task>> Get(string Future = m.Future, IamId = m.IamId, Ross = m.Ross, - Added = m.Added + Added = m.Added, + SpridenId = m.SpridenId, + Pidm = m.Pidm, + EmployeeId = m.EmployeeId, + VmacsId = m.VmacsId, + UnexId = m.UnexId, + MivId = m.MivId, }); }); + AaudUser? currentUser = UserHelper.GetCurrentUser(); + if (!UserHelper.HasPermission(_rapsContext, currentUser, "SVMSecure.DirectoryDetail")) + { + results.ForEach(r => + { + r.SpridenId = null; + r.Pidm = null; + r.EmployeeId = null; + r.VmacsId = null; + r.UnexId = null; + }); + } + results.ForEach(r => { - LdapUser l = new LdapService().GetUser(r.LoginId); + LdapUserContact l = new LdapService().GetUserContact(r.LoginId); r.Title = l.Title; r.Department = l.Department; + r.Phone = l.Phone; + r.Mobile = l.Mobile; + r.UserName = l.UserName; }); return results; diff --git a/web/Areas/Directory/Models/IndividualSearchResult.cs b/web/Areas/Directory/Models/IndividualSearchResult.cs index 7f47826..423df22 100644 --- a/web/Areas/Directory/Models/IndividualSearchResult.cs +++ b/web/Areas/Directory/Models/IndividualSearchResult.cs @@ -68,8 +68,8 @@ public class IndividualSearchResult public DateTime? Added { get; set; } = null!; - public bool Can_See_IDs { get; set; } = false; - public bool Can_Spoof { get; set; } = false; - public bool Can_See_UserInfo { get; set; } = false; + public string? Phone { get; set; } = null!; + public string? Mobile { get; set; } = null!; + public string? UserName { get; set; } = null!; } } diff --git a/web/Areas/Directory/Views/Index.cshtml b/web/Areas/Directory/Views/Index.cshtml index 79a1d04..51cc42c 100644 --- a/web/Areas/Directory/Views/Index.cshtml +++ b/web/Areas/Directory/Views/Index.cshtml @@ -3,8 +3,6 @@ @{ RAPSContext? rapsContext = (RAPSContext?)Context.RequestServices.GetService(typeof(RAPSContext)); IUserHelper UserHelper = new UserHelper(); -} -@{ ViewData["Title"] = "Directory"; }

Directory

@@ -32,7 +30,7 @@ no-results-label="No results found"> + +