Skip to content

Feature/userinfo - #273

Open
JasonRobertFrancis wants to merge 22 commits into
mainfrom
feature/userinfo
Open

Feature/userinfo#273
JasonRobertFrancis wants to merge 22 commits into
mainfrom
feature/userinfo

Conversation

@JasonRobertFrancis

Copy link
Copy Markdown
Contributor

No description provided.

@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov-commenter

codecov-commenter commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.83670% with 6328 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.18%. Comparing base (dd7f02c) to head (cfde89d).

Files with missing lines Patch % Lines
web/Areas/Directory/Services/UserInfoService.cs 41.52% 769 Missing and 38 partials ⚠️
web/Areas/Directory/Views/UserInfo.cshtml 0.00% 386 Missing ⚠️
web/Models/PPS/VwUserinfoUser.cs 0.00% 149 Missing ⚠️
web/Models/PPS/PsJobV.cs 0.00% 125 Missing ⚠️
web/Models/PPS/JobDV.cs 0.00% 121 Missing ⚠️
web/Models/PPS/JpmJpItemDV.cs 0.00% 121 Missing ⚠️
web/Models/PPS/PositionDV.cs 0.00% 118 Missing ⚠️
web/Models/PPS/PsJpmJpItemsV.cs 0.00% 116 Missing ⚠️
web/Models/PPS/EdbperVc.cs 0.00% 104 Missing ⚠️
web/Models/PPS/VwAllJobPosOrg.cs 0.00% 102 Missing ⚠️
... and 195 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #273      +/-   ##
==========================================
+ Coverage   50.07%   56.18%   +6.11%     
==========================================
  Files         998     1193     +195     
  Lines       58354    80337   +21983     
  Branches     5853     6165     +312     
==========================================
+ Hits        29221    45139   +15918     
- Misses      28228    34227    +5999     
- Partials      905      971      +66     
Flag Coverage Δ
backend 56.02% <27.83%> (+6.78%) ⬆️
frontend 58.45% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

{
var result = System.Text.Json.JsonSerializer.Deserialize<DateTime?>($"\"{input}\"", options);
Assert.NotNull(result);
var nonNullResult = result.Value;
new("scope", "api_access")
};

var formContent = new FormUrlEncodedContent(formParams);
Comment on lines +401 to +407
foreach (var name in nameList)
{
if (!string.IsNullOrEmpty(name.StudentName) && name.ActivityDate.HasValue)
{
names.Add($"{name.StudentName} ({name.ActivityDate:MM/dd/yyyy})");
}
}
Comment on lines +1721 to +1731
foreach (var middlePart in middleParts)
{
if (middlePart.Length > 0)
{
var variation = $"{name} {middlePart[0]}";
if (!nameVariations.Contains(variation))
{
nameVariations.Add(variation);
}
}
}
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Classes/Utilities/IamApi.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment on lines +1325 to +1340
foreach (var history in historyData)
{
var ucpathResult = new UCPathResult
{
JobCode = history.Jobcode,
JobCodeDescription = history.JobcodeDesc,
DepartmentId = history.Deptid,
DepartmentDescription = history.DeptDesc,
ActionDescription = history.ActionDescr,
PositionEffectiveDate = history.PositionEffdt.HasValue ? DateOnly.FromDateTime(history.PositionEffdt.Value) : null,
ReportsTo = GetReportsToName(history),
ReportsToPosition = GetReportsToPosition(history)
};

result.UCPathHistory.Add(ucpathResult);
}
Comment on lines +1643 to +1650
foreach (var groupDn in allGroups)
{
var formattedGroup = AdFormat(groupDn, domains);
if (!string.IsNullOrEmpty(formattedGroup))
{
result.ADMemberOf.Add(formattedGroup);
}
}
Comment on lines +683 to +712
foreach (var perm in permissions)
{
var parts = perm.Split('.');
var partsToProcess = parts.Skip(1).ToList();
if (!partsToProcess.Any())
{
continue;
}

var currentList = roots;
for (int i = 0; i < partsToProcess.Count; i++)
{
var part = partsToProcess[i];
var node = currentList.FirstOrDefault(n => n.Name == part);
if (node == null)
{
node = new PermissionNode
{
Name = part,
FullPath = string.Join('.', parts.Take(i + 2))
};
currentList.Add(node);
}
if (i == partsToProcess.Count - 1)
{
node.IsPermission = true;
}
currentList = node.Children;
}
}

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Areas/Directory/Services/UserInfoService.cs Fixed
Comment thread web/Classes/Utilities/IamApi.cs Fixed
@rlorenzo

Copy link
Copy Markdown
Contributor

@JasonRobertFrancis You should rebase this off the latest main branch since this doesn't have the GitHub Actions for code quality checks running

Comment on lines +1113 to +1121
foreach (var role in filteredRoles)
{
string displayName = role.DisplayName ?? role.Role;
result.SystemRoles.Add(new SystemRole
{
System = system,
DisplayName = FormatPermissionName(displayName)
});
}
Comment on lines +173 to +176
catch
{
return new List<string>();
}
Comment on lines +231 to +234
catch (Exception ex)
{
Console.WriteLine($"Warning: PopulateDirectoryInfoAsync LDAP failed: {ex.Message}");
}
@rlorenzo

Copy link
Copy Markdown
Contributor

@JasonRobertFrancis #261 just merged to main and conflicts with this branch.

Small scope: one file, web/Areas/Directory/Controllers/DirectoryController.cs, 4 regions. web/Program.cs and web/Viper.csproj auto-merge cleanly.

Both branches reworked the same methods, so every region is "main moved it" vs "your feature changed it in place":

  1. Class-level [Route]: we both replaced the per-action absolute routes with one class-level attribute. The only difference is a leading slash (/[area] vs [area]), which resolves to the same template. Take either.
  2. VMACS lookup (twice, once per search method): main extracts await AddVmacsContactInfoAsync(result); this branch inlines VMACSService.Search(...) and blocks on .Result. Main also turned individuals.ForEach(m => ...) into a foreach, which is what allows the await instead of the blocking call.
  3. Helpers: main added a shared SearchCurrentAaudUsers used by both Get and GetUCD (it clears a CodeQL cs/complex-condition); this branch added PopulateVmacsDetails and an OnActionExecutionAsync override.

The two sides are complementary rather than contradictory: keep main's structure, fold in your PopulateVmacsDetails body and OnActionExecutionAsync, and let the .Result go.

Optional: prompt for a coding agent
Resolve the merge conflict in web/Areas/Directory/Controllers/DirectoryController.cs
between main and feature/userinfo (4 conflict regions). Keep main's structure and
fold the feature work into it:

- Use main's shared SearchCurrentAaudUsers helper for both Get and GetUCD. Do not
  reintroduce the OR chain over identifiers: it trips CodeQL cs/complex-condition.
- Use main's foreach loops with await AddVmacsContactInfoAsync(result). Do not keep
  VMACSService.Search(...).Result; that blocking call only existed because you
  cannot await inside List.ForEach.
- Keep the feature branch's PopulateVmacsDetails body and its OnActionExecutionAsync
  override.
- For the class-level [Route], either form works; match main's "/[area]".

Then verify: npm run test:backend, and
npm run lint web/Areas/Directory/Controllers/DirectoryController.cs

@rlorenzo

rlorenzo commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands the Directory/UserInfo functionality and supporting infrastructure in the VIPER web app by adding new UI styling, new/adjusted endpoints for directory search and session timeout, and a large set of scaffolded EF model types + DbContext registrations for additional data domains (PPS, Keys, IDCards, EquipmentLoan).

Changes:

  • Add Directory “userinfo” styling and wire it into the shared layout.
  • Add a new /GetSessionTimeout JSON endpoint and adjust the SessionTimeout view component behavior in development.
  • Introduce new Directory controller APIs and a large set of new EF entity classes, plus DbContext registrations for additional schemas/apps; update LDAP/IAM utilities and tests/docs accordingly.

Reviewed changes

Copilot reviewed 218 out of 220 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
web/wwwroot/css/userinfo.css Adds styling for the Directory “userinfo” page.
web/Viper.csproj Adjusts build warning suppression configuration.
web/Views/Shared/Components/SessionTimeout/SessionTimeout.cs Updates session timeout refresh URL behavior.
web/Views/Shared/_VIPERLayout.cshtml Loads directory/userinfo CSS conditionally; normalizes helper variable naming.
web/Properties/launchSettings.json Changes local launch behavior (launchBrowser).
web/Program.cs Registers additional DbContexts and HTTP client services.
web/Models/PPS/VwStipend.cs Scaffolded PPS EF entity.
web/Models/PPS/VwRankStepEthnicityPayRate.cs Scaffolded PPS EF entity.
web/Models/PPS/VwPersonAccrual.cs Scaffolded PPS EF entity.
web/Models/PPS/VwPerson.cs Scaffolded PPS EF entity.
web/Models/PPS/VwLoa.cs Scaffolded PPS EF entity.
web/Models/PPS/VwJobCodeAndGroup.cs Scaffolded PPS EF entity.
web/Models/PPS/VwEmployee.cs Scaffolded PPS EF entity.
web/Models/PPS/VwAccrualsCdm.cs Scaffolded PPS EF entity.
web/Models/PPS/VwAccrual.cs Scaffolded PPS EF entity.
web/Models/PPS/VisaPermitDataDV.cs Scaffolded PPS EF entity.
web/Models/PPS/UpdateLog.cs Scaffolded PPS EF entity.
web/Models/PPS/UnionDV.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathVerificationItem.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathOverride.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathmissingpersonBk.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathMissingPerson20190821.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathMissingPerson.cs Scaffolded PPS EF entity.
web/Models/PPS/UcpathItemNote.cs Scaffolded PPS EF entity.
web/Models/PPS/UcdOrganizationDV.cs Scaffolded PPS EF entity.
web/Models/PPS/UcdFauDV.cs Scaffolded PPS EF entity.
web/Models/PPS/UcdEmployeeFlagsDV.cs Scaffolded PPS EF entity.
web/Models/PPS/UcdEmployeeFlagsDOverride.cs Scaffolded PPS EF entity.
web/Models/PPS/TitlecodeGroup.cs Scaffolded PPS EF entity.
web/Models/PPS/TimeDailyDV.cs Scaffolded PPS EF entity.
web/Models/PPS/ServiceCreditUnit.cs Scaffolded PPS EF entity.
web/Models/PPS/ServiceCredit.cs Scaffolded PPS EF entity.
web/Models/PPS/PsxlatitemV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsVisaPmtDataV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUnionTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcSsDisclosurV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcJobGrpDescV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcJobCodeTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcJobCodesV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcFundAttribV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcExtSystemV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcdDmPsNamesPrefVnamesV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcCtoOscV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcAmSsTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsUcAmSsRcdV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsPrimaryJobsV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsPersonV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsPersonalPhoneV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsPersDataEffdtV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsPerOrgInstV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsJpmProfileV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsJpmCatTypesV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsJpmCatItemsV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsGpRsltAcumV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsGpAbsReasonV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsGpAbsEaV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsGpAbsEaStaV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsEthnicGrpTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsErnProgramTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsEmplClassTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsEmailAddressesV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsEarningsBalV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsDiversEthnicV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsDeptTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsDeptBudgetErnV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsDeptBudgetDtV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsCountryTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsCompRatecdTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsCompensationV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsCitizenStsTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsCitizenshipV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsAddressTypTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsAddressesV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsAddlPayDataV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsActnReasonTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsActionTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/PsAcctCdTblV.cs Scaffolded PPS EF entity.
web/Models/PPS/Prj2EmpidToPpsid.cs Scaffolded PPS EF entity.
web/Models/PPS/PrimaryJobsDV.cs Scaffolded PPS EF entity.
web/Models/PPS/OrganizationDV.cs Scaffolded PPS EF entity.
web/Models/PPS/OdsEmployeeFlagsDV.cs Scaffolded PPS EF entity.
web/Models/PPS/JpmFV.cs Scaffolded PPS EF entity.
web/Models/PPS/JobStatusDV.cs Scaffolded PPS EF entity.
web/Models/PPS/JobOverride.cs Scaffolded PPS EF entity.
web/Models/PPS/JobHistoryDetail.cs Scaffolded PPS EF entity.
web/Models/PPS/JobHistory.cs Scaffolded PPS EF entity.
web/Models/PPS/JobActionDV.cs Scaffolded PPS EF entity.
web/Models/PPS/HistServiceCredit.cs Scaffolded PPS EF entity.
web/Models/PPS/HistloaV.cs Scaffolded PPS EF entity.
web/Models/PPS/GoAnywhereLog.cs Scaffolded PPS EF entity.
web/Models/PPS/FurloughTarget.cs Scaffolded PPS EF entity.
web/Models/PPS/Export.cs Scaffolded PPS EF entity.
web/Models/PPS/EthnicityGender20210201.cs Scaffolded PPS EF entity.
web/Models/PPS/EthnicityGender.cs Scaffolded PPS EF entity.
web/Models/PPS/EmployeeHistory.cs Scaffolded PPS EF entity.
web/Models/PPS/EdbeffrptLog.cs Scaffolded PPS EF entity.
web/Models/PPS/Edbeffrpt.cs Scaffolded PPS EF entity.
web/Models/PPS/Dvtloa.cs Scaffolded PPS EF entity.
web/Models/PPS/DiversEthnicityDV.cs Scaffolded PPS EF entity.
web/Models/PPS/DepartmentDV.cs Scaffolded PPS EF entity.
web/Models/PPS/DepartmentBudgetEarnFV.cs Scaffolded PPS EF entity.
web/Models/PPS/Ctvhme.cs Scaffolded PPS EF entity.
web/Models/PPS/Ctltci.cs Scaffolded PPS EF entity.
web/Models/PPS/Ctlcad.cs Scaffolded PPS EF entity.
web/Models/PPS/CompensationFV.cs Scaffolded PPS EF entity.
web/Models/PPS/CompensationDV.cs Scaffolded PPS EF entity.
web/Models/PPS/CompensationDRateV.cs Scaffolded PPS EF entity.
web/Models/PPS/AuditOverride.cs Scaffolded PPS EF entity.
web/Models/PPS/AccountCodeDV.cs Scaffolded PPS EF entity.
web/Models/PPS/AbsenceResultFV.cs Scaffolded PPS EF entity.
web/Models/PPS/AbsenceResultDV.cs Scaffolded PPS EF entity.
web/Models/PPS/AbsenceEventFV.cs Scaffolded PPS EF entity.
web/Models/PPS/AbsenceEventDV.cs Scaffolded PPS EF entity.
web/Models/PPS/AbsenceCalendarDV.cs Scaffolded PPS EF entity.
web/Models/Keys/VwUserinfo.cs Scaffolded Keys EF entity/view model.
web/Models/Keys/KeyManager.cs Scaffolded Keys EF entity.
web/Models/Keys/KeyBuilding.cs Scaffolded Keys EF entity.
web/Models/Keys/KeyAssignment.cs Scaffolded Keys EF entity.
web/Models/Keys/Key.cs Scaffolded Keys EF entity.
web/Models/Keys/Import3.cs Scaffolded Keys import EF entity.
web/Models/Keys/Import2.cs Scaffolded Keys import EF entity.
web/Models/Keys/Import.cs Scaffolded Keys import EF entity.
web/Models/Keys/Disposition.cs Scaffolded Keys EF entity.
web/Models/Keys/Building.cs Scaffolded Keys EF entity.
web/Models/IDCards/VwLatestIdcard.cs Scaffolded IDCards EF view entity.
web/Models/IDCards/VwDelimitedSpecialApprover.cs Scaffolded IDCards EF view entity.
web/Models/IDCards/VwApproverMothraId.cs Scaffolded IDCards EF view entity.
web/Models/IDCards/PrintQueue.cs Scaffolded IDCards EF entity (with navigation).
web/Models/IDCards/PhotoExport.cs Scaffolded IDCards EF entity.
web/Models/IDCards/LenelBadge.cs Scaffolded IDCards EF entity.
web/Models/IDCards/IgnoreList.cs Scaffolded IDCards EF entity.
web/Models/IDCards/IdCardToPrintQueue.cs Scaffolded join entity.
web/Models/IDCards/IdCard.cs Scaffolded IDCards EF entity.
web/Models/IDCards/ExtVisit.cs Scaffolded IDCards EF entity.
web/Models/IDCards/EcoTimeBadgeExclusion.cs Scaffolded IDCards EF entity.
web/Models/IDCards/DvtSvmUnit.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtSpecialty.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtSpecialApprover.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtReason.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtOverseer.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtClient.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtCardStatus.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/DvtApprover.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/Defuncted.cs Scaffolded IDCards EF entity.
web/Models/IDCards/DbIdCard.cs Scaffolded IDCards EF entity.
web/Models/IDCards/BulkLoadResult.cs Scaffolded IDCards EF entity.
web/Models/IDCards/Audit.cs Scaffolded IDCards audit EF entity.
web/Models/IDCards/AccessLevel.cs Scaffolded IDCards lookup entity.
web/Models/IDCards/AccessExpiration.cs Scaffolded IDCards EF entity.
web/Models/EquipmentLoan/VwLoan.cs Scaffolded EquipmentLoan EF view entity.
web/Models/EquipmentLoan/Reason.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/O.cs Scaffolded EquipmentLoan EF entity (OS lookup).
web/Models/EquipmentLoan/LoanNote.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/LoanItem.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/Loan.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/EmailTemplate.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/EmailSent.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/Audit.cs Scaffolded EquipmentLoan audit EF entity.
web/Models/EquipmentLoan/AssetType.cs Scaffolded EquipmentLoan lookup entity.
web/Models/EquipmentLoan/AssetNote.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/Asset.cs Scaffolded EquipmentLoan EF entity.
web/Models/EquipmentLoan/AppSetting.cs Scaffolded EquipmentLoan EF entity.
web/Controllers/HomeController.cs Adds a GetSessionTimeout endpoint.
web/Classes/Utilities/LdapService.cs Adds MothraID lookup and improves sorting; extends LDAP helpers.
web/Classes/Utilities/IamApi.cs Adds custom DateTime parsing for IAM API responses.
web/Classes/AreaController.cs Adds PopulateLeftNav helper and adjusts dev nav link conversion.
web/Areas/Directory/Models/UCPathResult.cs Adds Directory UCPath DTO.
web/Areas/Directory/Models/LoanResult.cs Adds Directory loan DTO.
web/Areas/Directory/Models/LdapUserContact.cs Adjusts LDAP contact model (MothraId nullable).
web/Areas/Directory/Models/KeyResult.cs Adds Directory key DTO.
web/Areas/Directory/Models/InstinctResult.cs Adds Directory instinct DTO.
web/Areas/Directory/Models/IndividualSearchResultWithIDs.cs Adjusts Directory search result mapping.
web/Areas/Directory/Models/IndividualSearchResult.cs Adjusts Directory search result mapping.
web/Areas/Directory/Models/IDCardResult.cs Adds Directory ID card DTO.
web/Areas/Directory/Models/DirectoryUser.cs Adds view model for Directory permissions/features.
web/Areas/Directory/Controllers/DirectoryController.cs Adds query-based search endpoint and left-nav population.
Viper.sln.DotSettings Updates ReSharper inspection severities.
test/Usings.cs Disables xUnit test parallelization globally.
test/Areas/Directory/VMACSServiceTest.cs Updates VMACS URL validation test cases.
CLAUDE.md Updates repo contribution/testing guidance.
.gitignore Updates ignore patterns (currently includes conflict marker and broad ignores).

Comment on lines +13 to +15
ViewData["sessionRefreshUrl"] = onDev
? "/GetSessionTimeout"
: ("https://" + HttpHelper.HttpContext?.Request.Host.Value + "/")
{
return NotFound();
}
var secondsLeft = (int)(timeout.SessionTimeoutDateTime - DateTime.Now).TotalSeconds;
Comment on lines +127 to +129
if (id == null) return null;
string filter = string.Format("(ucdpersonuuid = {0})", id);
var results = SearchLdap(filter);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonRobertFrancis Yes, this was flagged in the previous PR and still an issue

Comment on lines 99 to +102
Email = ldapUserContact.Mail;
MailId = ldapUserContact.Mail?.Split("@")[0];
MailId = ldapUserContact.Mail.Split("@")[0];
UserName = ldapUserContact.Uid;
PostalAddress = (ldapUserContact.PostalAddress ?? "").Replace("$", '\n'.ToString());
PostalAddress = ldapUserContact.PostalAddress.Replace("$", '\n'.ToString());
Mobile = ldapUserContact.Mobile;
UserName = ldapUserContact.Uid;
PostalAddress = (ldapUserContact.PostalAddress ?? "").Replace("$", '\n'.ToString());
PostalAddress = ldapUserContact.PostalAddress.Replace("$", '\n'.ToString());
Comment thread web/Viper.csproj
<ImplicitUsings>enable</ImplicitUsings>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<PublishDir>bin\Release\net10.0\publish\</PublishDir>
<NoWarn>$(NoWarn);NU1902;NU1608</NoWarn>
.userinfo ul + ul { margin-top: 0.5rem; }
.userinfo ul li { padding: 0.2rem; margin-left: -0.2rem; }
.userinfo ul li:nth-child(odd) { background-color: #f1f3f5; }
.userinfo ul li ul li { padding-left: -0.2rem; margin-left: 2.2rem; }

@bsedwards bsedwards left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address the student information in the user info page - we need to remove sensitive information.

Other things I've noticed:

  • I was using the directory on secure-test earlier, but now when I search I'm getting an HTML page saying my session has timed out.
Image
  • I was missing the AAUD Check, ID Check, and UC Path info links on directory entries.
  • Missing AD information from user info page.
  • Student information on the user info page only shows 5 flags for me. Please validate the permissions being checked.
Image Image

return null;
}

Console.WriteLine($"[INSTINCT SERVICE] mothraId: '{mothraId}', iamId: '{iamId}', result.MothraId: '{result.MothraId}'");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we should have console.writeline in production code

/// <summary>
/// Get cumulative GPA - equivalent to getCumulativeGPA in SIS.cfc
/// </summary>
private async Task<string?> GetStudentCumulativeGPAAsync(string pidm, string termCode, string majorCode)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this is reimplementing every function in SIS.cfc inside UserInfoService.cs. I'm not sure how many of these functions the user info page is actually using (ref previous comment on sensitivity of this info). I suggest implementing these functions in a separate class and having the UserInfoService only reference what is needed and approved for display on the UserInfoPage.

result.StudentBannerId = await GetStudentBannerIdAsync(result.Pidm);
result.StudentConfidential = await IsStudentConfidentialAsync(result.Pidm);
result.StudentConfidentialScope = await GetStudentConfidentialScopeAsync(result.Pidm);
result.StudentBirthDate = await GetStudentBirthDateAsync(result.Pidm);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these shown on the User Info page? Many of these fields are sensitive and should only be shown to a small number of users.

public static async Task<byte[]?> GetUserPhotoAsync(string mailId, bool useAltPhoto = false)
{
// stubbed
return null;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

??

var formContent = new FormUrlEncodedContent(formParams);
Console.WriteLine("[INSTINCT AUTH] Sending token POST request...");
var response = await httpClient.PostAsync(tokenUrl, formContent);
Console.WriteLine($"[INSTINCT AUTH] Response Status Code: {response.StatusCode}");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More Console.WriteLine

}
<li><strong>Dual Degree?:</strong> @(Model.StudentIsDualDegree ? "Yes" : "No")</li>

@if (Model.CanViewStudentID)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure being able to view the student ID is sufficient permission to view this detail. I would suggest leaving out things like GPA, residency and citizenship, ethnicity, age, etc. off the UserInfo page entirely.

<li><strong>Student Employer:</strong> @Model.StudentEmployer</li>
}
}
@if (!string.IsNullOrEmpty(Model.StudentPermanentAddress))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also leave addresses out of this page

Comment thread web/Viper.csproj
<ImplicitUsings>enable</ImplicitUsings>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<PublishDir>bin\Release\net10.0\publish\</PublishDir>
<NoWarn>$(NoWarn);NU1902;NU1608</NoWarn>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to disable warnings on these issues?

Comment thread .gitignore
# Used to reduce unnecessary dotnet builds
.build-cache/

<<<<<<< HEAD

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line created during a merge?

Comment thread .gitignore

# Agent folders
.claude
CLAUDE.md

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to exclude the .md files? I think Rex committed some.

userInfo.CanViewInstinct = ownPage || _userHelper.HasPermission(_rapsContext, currentUser, "SVMSecure.userinfo.instinct");
userInfo.CanViewADGroups = _userHelper.HasPermission(_rapsContext, currentUser, "SVMSecure.UserInfo.ADGroups");

userInfo.CanViewDirectoryDetail = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonRobertFrancis The permission overrides are still in place. This has got to be debugging code, right?

Comment on lines +127 to +129
if (id == null) return null;
string filter = string.Format("(ucdpersonuuid = {0})", id);
var results = SearchLdap(filter);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonRobertFrancis Yes, this was flagged in the previous PR and still an issue

return NotFound();
}

[Route("/[area]/nav")]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JasonRobertFrancis This will conflict with the /nav route for the Directory. Go to https://secure-test.vetmed.ucdavis.edu/2/Directory/nav and it will give an error that it doesn't on main/PROD.

<img src="https://viper.vetmed.ucdavis.edu/public/utilities/getbase64image.cfm?mailid=@Model.MailId" width="87" height="111" alt="User Photo" />
</span>
<span class="photo2">
<img src="https://viper.vetmed.ucdavis.edu/public/utilities/getbase64image.cfm?mailid=@Model.MailId&amp;altphoto=1" width="87" height="111" alt="Alternative Photo" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be hardcoded to prod VIPER1, right?

<div class="direct">
<h2>User Information Page</h2>
<span class="photo">
<img src="https://viper.vetmed.ucdavis.edu/public/utilities/getbase64image.cfm?mailid=@Model.MailId" width="87" height="111" alt="User Photo" />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be hardcoded to prod VIPER1, right?

{
<li><strong>Banner ID:</strong> @Model.StudentBannerId</li>
}
<li><strong>Confidential?:</strong> @(Model.StudentConfidential ? "Yes" : "No")</li>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag is fetched, but it does not appear to be used to suppress any data. Is that right?

Comment thread .gitignore
CLAUDE.md

# Temp Agent Files
DESIGN.md*

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should not be ignored either

"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"launchBrowser": true,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed. If you use npm run dev the browser will be opened for you

</article>

@section Scripts {
<script src="~/js/userinfo.js"></script>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This JS file does not exist

{
if (vm?.item != null)
{
if (vm.item.Nextel != null) result.Nextel = vm.item.Nextel[0];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An empty value for Nextel/LDPager/Unit can crash the search.

Suggested code:

private static void PopulateVmacsDetails(IndividualSearchResult result, VMACSQuery? vm)
{
    var item = vm?.item;
    if (item == null)
    {
        return;
    }
    if (item.Nextel is { Length: > 0 }) result.Nextel = item.Nextel[0];
    if (item.LDPager is { Length: > 0 }) result.LDPager = item.LDPager[0];
    if (item.Unit is { Length: > 0 }) result.Department = item.Unit[0];
}

.ThenByDescending(p => p.Effdt)
.ToListAsync();

foreach (var history in historyData)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a Claude code analysis:

N+1 with synchronous blocking EF calls in an async request
UserInfoService.cs:1333-1348 calls GetReportsToName(history) and GetReportsToPosition(history) per UCPath history row. Each helper (1359-1433) issues up to two FirstOrDefault() calls — synchronous, not FirstOrDefaultAsync — at lines 1367, 1377, 1405, 1416. And the two helpers query the identical rows independently, so it's up to 4 blocking round-trips per row where 1 async batch would do. A person with 30 job actions is 120 sequential blocking DB calls holding a thread-pool thread.

Also:

  • PopulateKeysAsync:1498 — one AAUD query per key assignment to resolve IssuedBy
  • GetUserPermissionsForSystemAsync — called 5× from 1126-1136, re-running the same two large RAPS joins each time; only the trailing StartsWith(systemPrefix) filter differs
  • No .AsNoTracking() anywhere in a 2,229-line entirely read-only service

Seems like a major performance concern.

[Route("")]
public ActionResult Index()
{
return Redirect("/Directory");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would go back to VIPER1, is that expected?

@if (Model.CanViewRoles && Model.SystemRoles.Any())
{
<div class="roles">
<h2><a href="/RAPS/" target="_blank">System Roles</a></h2>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would go back to VIPER1, is that expected?

}
if (!foundMatch)
{
result.ErrorMessage = $"User found in API but no name match. Variations tried: {string.Join(", ", nameVariations)}. API users: {string.Join(", ", graphqlResponse.Data.SearchUsers.Select(u => $"{u.NameFirst} {u.NameLast}"))}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When this returns no matches, it returns a list of API users. This seems like it would leak user information.

// Create GraphQL query
var query = $@"
query {{
searchUsers(name: ""{lastName}"") {{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GraphQL query built by string interpolation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants