Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Core/Resgrid.Config/OidcConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@ public static class OidcConfig

public static string Key = "";

public static string ConnectionString = "Server=rgdevserver;Database=ResgridOIDC;User Id=resgrid_odic;Password=resgrid123;MultipleActiveResultSets=True;TrustServerCertificate=True;";
public static string ConnectionString = "";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Mutable public configuration state in Core/Resgrid.Config/OidcConfig.cs allows accidental reassignment of ConnectionString and obscures immutability intent, with the same issue also present at Core/Resgrid.Config/OidcConfig.cs:28-28, Core/Resgrid.Config/SessionSecurityConfig.cs:7-20,22, Tests/Resgrid.Tests/Services/ClientSessionMetadataParserTests.cs:9-9, and Core/Resgrid.Services/DepartmentSettingsService.cs:27-27,40-40. Mark the field readonly, or const if compile-time constant.

Kody rule violation: Use `readonly` or `const` for Immutable Data

public static readonly string ConnectionString = string.Empty;
Prompt for LLM

File Core/Resgrid.Config/OidcConfig.cs:

Line 16:

Mutable public configuration state in Core/Resgrid.Config/OidcConfig.cs allows accidental reassignment of ConnectionString and obscures immutability intent, with the same issue also present at Core/Resgrid.Config/OidcConfig.cs:28-28, Core/Resgrid.Config/SessionSecurityConfig.cs:7-20,22, Tests/Resgrid.Tests/Services/ClientSessionMetadataParserTests.cs:9-9, and Core/Resgrid.Services/DepartmentSettingsService.cs:27-27,40-40. Mark the field readonly, or const if compile-time constant.

Suggested Code:

public static readonly string ConnectionString = string.Empty;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


public static int AccessTokenExpiryMinutes = 1440;

public static int RefreshTokenExpiryDays = 365;

public static int NonMobileRefreshTokenExpiryDays = 2;

/// <summary>
/// Comma-separated, registered client IDs allowed to receive the longer mobile
/// refresh-token lifetime. Anonymous requests and caller-supplied scopes never qualify.
/// </summary>
public static string TrustedLongLivedClientIds = "";

public static string EncryptionCert = "";

public static string SigningCert = "";
Expand Down
24 changes: 24 additions & 0 deletions Core/Resgrid.Config/SessionSecurityConfig.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace Resgrid.Config
{
public static class SessionSecurityConfig
{
// Session tracking is required by the Web BFF and is safe for pre-feature
// credentials because they are adopted lazily by the validation middleware.
public static bool TrackingEnabled = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Immutability issue in Core/Resgrid.Config/SessionSecurityConfig.cs: TrackingEnabled and the declarations at lines 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20, and 22 are initialized with compile-time constants but remain mutable. Mark these values const, or readonly if runtime-only assignment is required, to prevent accidental reassignment.

Kody rule violation: Use `readonly` or `const` for Immutable Data

public const bool TrackingEnabled = true;
Prompt for LLM

File Core/Resgrid.Config/SessionSecurityConfig.cs:

Line 7:

Immutability issue in Core/Resgrid.Config/SessionSecurityConfig.cs: TrackingEnabled and the declarations at lines 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 20, and 22 are initialized with compile-time constants but remain mutable. Mark these values const, or readonly if runtime-only assignment is required, to prevent accidental reassignment.

Suggested Code:

public const bool TrackingEnabled = true;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Immutable configuration in Core/Resgrid.Config/SessionSecurityConfig.cs uses a compile-time constant as a mutable field, which obscures intent and permits accidental reassignment at line 8 and related declarations at lines 13-20, Core/Resgrid.Config/OidcConfig.cs:16 and :28, and Web/Resgrid.Web/Models/AccountViewModels/ResetPasswordViewModel.cs:20. Mark these values as const, or static readonly if type initialization-time assignment is required.

Kody rule violation: Use `readonly` or `const` for Immutable Data

public const bool TrackingEnabled = true;
Prompt for LLM

File Core/Resgrid.Config/SessionSecurityConfig.cs:

Line 7:

Immutable configuration in Core/Resgrid.Config/SessionSecurityConfig.cs uses a compile-time constant as a mutable field, which obscures intent and permits accidental reassignment at line 8 and related declarations at lines 13-20, Core/Resgrid.Config/OidcConfig.cs:16 and :28, and Web/Resgrid.Web/Models/AccountViewModels/ResetPasswordViewModel.cs:20. Mark these values as const, or static readonly if type initialization-time assignment is required.

Suggested Code:

public const bool TrackingEnabled = true;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Immutable configuration misuse identified in Core/Resgrid.Config/SessionSecurityConfig.cs because TrackingEnabled is initialized with a compile-time constant but remains mutable. Mark TrackingEnabled as const, and apply the same treatment to the related fields at Core/Resgrid.Config/SessionSecurityConfig.cs:8-8, :9-9, :12-12, :13-13, :14-14, :15-15, :16-16, :17-17, :18-18, :19-19, :20-20, :22-22, Core/Resgrid.Config/OidcConfig.cs:16-16, Core/Resgrid.Config/OidcConfig.cs:28-28, and Core/Resgrid.Services/EmailService.cs:87-87 to prevent accidental mutation.

Kody rule violation: Use `readonly` or `const` for Immutable Data

public const bool TrackingEnabled = true;
Prompt for LLM

File Core/Resgrid.Config/SessionSecurityConfig.cs:

Line 7:

Immutable configuration misuse identified in `Core/Resgrid.Config/SessionSecurityConfig.cs` because `TrackingEnabled` is initialized with a compile-time constant but remains mutable. Mark `TrackingEnabled` as `const`, and apply the same treatment to the related fields at `Core/Resgrid.Config/SessionSecurityConfig.cs:8-8`, `:9-9`, `:12-12`, `:13-13`, `:14-14`, `:15-15`, `:16-16`, `:17-17`, `:18-18`, `:19-19`, `:20-20`, `:22-22`, `Core/Resgrid.Config/OidcConfig.cs:16-16`, `Core/Resgrid.Config/OidcConfig.cs:28-28`, and `Core/Resgrid.Services/EmailService.cs:87-87` to prevent accidental mutation.

Suggested Code:

public const bool TrackingEnabled = true;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

public static bool LegacyAdoptionEnabled = true;
public static string RequireSessionClaimForCredentialsIssuedAfterUtc = "";
// Blank is intentionally disabled at launch. Set to an ISO-8601 UTC timestamp
// only after previewing stored DepartmentSecurityPolicy session values.
public static string DepartmentSessionPolicyEnforcementAfterUtc = "";
public static int LastActivityWriteIntervalMinutes = 5;
public static int RevokedSessionRetentionDays = 90;
public static int PublicResetLinkLifetimeMinutes = 30;
public static int PublicResetAccountLimitPerHour = 3;
public static int PublicResetIpLimitPerHour = 10;
public static int WebBffAccessTokenLifetimeMinutes = 5;
public static int ClientMetadataMaximumLength = 256;
public static int UserAgentMaximumLength = 1024;
// Optional local JSON CIDR database. Leave blank to display location as unavailable.
public static string IpLocationDatabasePath = "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<data name="PageHeader" xml:space="preserve"><value>Password Expired</value></data>
<data name="ExpiredTitle" xml:space="preserve"><value>Your password has expired and must be changed before you can continue.</value></data>
<data name="AlertWarningText" xml:space="preserve"><value>Your department's security policy requires that passwords be changed periodically. Please choose a new password to continue.</value></data>
<data name="SessionRevocationWarning" xml:space="preserve"><value>Changing your password will log you out of every Resgrid session and revoke all access and refresh tokens.</value></data>
<data name="RequirementsHeader" xml:space="preserve"><value>Password Requirements</value></data>
<data name="ReqLength" xml:space="preserve"><value>At least {0} characters long</value></data>
<data name="ReqDigit" xml:space="preserve"><value>At least one digit (number)</value></data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1080,4 +1080,10 @@
<data name="UnitStatusBaseType_8" xml:space="preserve">
<value>Returning</value>
</data>
</root>
<data name="RequirePasswordResetViaEmail" xml:space="preserve">
<value>Require password resets by email</value>
</data>
<data name="RequirePasswordResetViaEmailHelp" xml:space="preserve">
<value>Prevents department and group administrators from choosing or seeing a member's new password. Reset actions send the member a short-lived, single-use email link instead. Existing sessions are revoked after the member successfully changes the password.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -675,4 +675,10 @@
<data name="UnitStatusBaseType_8" xml:space="preserve">
<value>Returning</value>
</data>
</root>
<data name="RequirePasswordResetViaEmail" xml:space="preserve">
<value>Require password resets by email</value>
</data>
<data name="RequirePasswordResetViaEmailHelp" xml:space="preserve">
<value>Prevents department and group administrators from choosing or seeing a member's new password. Reset actions send the member a short-lived, single-use email link instead. Existing sessions are revoked after the member successfully changes the password.</value>
</data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.ar.resx
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>على الأقل {0} أحرف</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>رقم واحد على الأقل</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>حرف كبير وحرف صغير على الأقل</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>إرسال بريد إلكتروني لإعادة تعيين كلمة المرور</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>إرسال بريد إلكتروني لإعادة تعيين كلمة المرور</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>سيرسل Resgrid إلى هذا المستخدم رابطًا قصير الصلاحية يُستخدم مرة واحدة إلى عنوان بريده الإلكتروني المؤكد. لن يختار المسؤول كلمة المرور الجديدة ولن يراها.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Mindestens {0} Zeichen lang</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Mindestens eine Ziffer (Zahl)</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Mindestens ein Groß- und ein Kleinbuchstabe</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>E-Mail zum Zurücksetzen des Passworts senden</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>E-Mail zum Zurücksetzen des Passworts senden</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid sendet diesem Benutzer einen kurzlebigen, einmal verwendbaren Link an seine bestätigte E-Mail-Adresse. Der Administrator wählt oder sieht das neue Passwort nicht.</value></data>
</root>
5 changes: 4 additions & 1 deletion Core/Resgrid.Localization/Areas/User/Profile/Profile.el.resx
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Τουλάχιστον {0} χαρακτήρες</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Τουλάχιστον ένα ψηφίο (αριθμός)</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Τουλάχιστον ένα κεφαλαίο και ένα πεζό γράμμα</value></data>
</root>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Αποστολή email επαναφοράς κωδικού πρόσβασης</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Αποστολή email επαναφοράς κωδικού πρόσβασης</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Το Resgrid θα στείλει σε αυτόν τον χρήστη έναν σύνδεσμο σύντομης διάρκειας και μίας χρήσης στην επιβεβαιωμένη διεύθυνση email του. Ο διαχειριστής δεν θα επιλέξει ούτε θα δει τον νέο κωδικό πρόσβασης.</value></data>
</root>
5 changes: 4 additions & 1 deletion Core/Resgrid.Localization/Areas/User/Profile/Profile.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>At least {0} characters long</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>At least one digit (number)</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>At least one uppercase and one lowercase letter</value></data>
</root>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Send Password Reset Email</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Send Password Reset Email</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid will send this user a short-lived, single-use link at their confirmed email address. The administrator will not choose or see the new password.</value></data>
</root>
5 changes: 4 additions & 1 deletion Core/Resgrid.Localization/Areas/User/Profile/Profile.es.resx
Original file line number Diff line number Diff line change
Expand Up @@ -300,4 +300,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Al menos {0} caracteres</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Al menos un dígito (número)</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Al menos una letra mayúscula y una minúscula</value></data>
</root>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Enviar correo de restablecimiento de contraseña</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Enviar correo de restablecimiento de contraseña</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid enviará a este usuario un enlace de corta duración y de un solo uso a su dirección de correo confirmada. El administrador no elegirá ni verá la nueva contraseña.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.fr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Au moins {0} caractères</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Au moins un chiffre</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Au moins une lettre majuscule et une minuscule</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Envoyer l’e-mail de réinitialisation du mot de passe</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Envoyer l’e-mail de réinitialisation du mot de passe</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid enverra à cet utilisateur un lien à courte durée de vie et à usage unique à son adresse e-mail confirmée. L’administrateur ne choisira ni ne verra le nouveau mot de passe.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.it.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Almeno {0} caratteri</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Almeno una cifra (numero)</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Almeno una lettera maiuscola e una minuscola</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Invia email di reimpostazione password</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Invia email di reimpostazione password</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid invierà a questo utente un link di breve durata e monouso al suo indirizzo email confermato. L’amministratore non sceglierà né vedrà la nuova password.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.pl.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Co najmniej {0} znaków</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Co najmniej jedna cyfra</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Co najmniej jedna wielka i jedna mała litera</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Wyślij wiadomość e-mail do resetowania hasła</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Wyślij wiadomość e-mail do resetowania hasła</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid wyśle temu użytkownikowi krótkotrwały, jednorazowy link na potwierdzony adres e-mail. Administrator nie wybierze ani nie zobaczy nowego hasła.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.sv.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Minst {0} tecken</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Minst en siffra</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Minst en stor och en liten bokstav</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Skicka e-post för lösenordsåterställning</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Skicka e-post för lösenordsåterställning</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid skickar en kortlivad engångslänk till användarens bekräftade e-postadress. Administratören väljer eller ser inte det nya lösenordet.</value></data>
</root>
3 changes: 3 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Profile/Profile.uk.resx
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,7 @@
<data name="PasswordRequirementsLength" xml:space="preserve"><value>Щонайменше {0} символів</value></data>
<data name="PasswordRequirementsDigit" xml:space="preserve"><value>Щонайменше одна цифра</value></data>
<data name="PasswordRequirementsCase" xml:space="preserve"><value>Щонайменше одна велика та одна мала літера</value></data>
<data name="SendPasswordResetEmailHeader" xml:space="preserve"><value>Надіслати електронний лист для скидання пароля</value></data>
<data name="SendPasswordResetEmail" xml:space="preserve"><value>Надіслати електронний лист для скидання пароля</value></data>
<data name="SendPasswordResetEmailInfo" xml:space="preserve"><value>Resgrid надішле цьому користувачеві короткочасне одноразове посилання на підтверджену електронну адресу. Адміністратор не вибиратиме й не бачитиме новий пароль.</value></data>
</root>
4 changes: 3 additions & 1 deletion Core/Resgrid.Model/AuditLogTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ public enum AuditLogTypes
ModerationReportSubmitted,
ModerationRequestReopened,
ModerationRequestCompleted,
ModerationEvidenceDownloaded
ModerationEvidenceDownloaded,
PasswordResetByAdministrator,
UserAuthenticationSessionsRevoked
}
}
6 changes: 6 additions & 0 deletions Core/Resgrid.Model/DepartmentSettingTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,11 @@ public enum DepartmentSettingTypes
/// before the board highlights it.
/// </summary>
UnitStatusThresholds = 62,

/// <summary>
/// When enabled, department and group administrators cannot choose a member's new password.
/// Their reset action sends the member the hardened, single-use password recovery link instead.
/// </summary>
RequirePasswordResetViaEmail = 63,
}
}
7 changes: 7 additions & 0 deletions Core/Resgrid.Model/Events/AuditEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ public class AuditEvent
[ProtoMember(11)]
public bool Successful { get; set; }

/// <summary>
/// The user the action was performed on, when that differs from the actor. Recorded on the audit
/// row as ObjectId so a privileged action can be queried by its subject and not just its actor.
/// </summary>
[ProtoMember(12)]
public string TargetUserId { get; set; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Nullability mismatch in Core/Resgrid.Model/Events/AuditEvent.cs leaves TargetUserId undefined for events without a distinct target user, which can trigger downstream NullReferenceException in callers across the listed usage sites. Model TargetUserId as string? or initialize it to string.Empty to make the contract explicit.

Kody rule violation: Add null checks to prevent NullReferenceException

		public string? TargetUserId { get; set; }
// or
		public string TargetUserId { get; set; } = string.Empty;
Prompt for LLM

File Core/Resgrid.Model/Events/AuditEvent.cs:

Line 47:

Nullability mismatch in Core/Resgrid.Model/Events/AuditEvent.cs leaves TargetUserId undefined for events without a distinct target user, which can trigger downstream NullReferenceException in callers across the listed usage sites. Model TargetUserId as string? or initialize it to string.Empty to make the contract explicit.

Suggested Code:

		public string? TargetUserId { get; set; }
// or
		public string TargetUserId { get; set; } = string.Empty;

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.


public AuditEvent()
{
EventId = Guid.NewGuid().ToString();
Expand Down
11 changes: 11 additions & 0 deletions Core/Resgrid.Model/ExternalIdentityLinkMethod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Resgrid.Model
{
public enum ExternalIdentityLinkMethod
{
Subject = 0,
VerifiedEmail = 1,
TrustedSamlEmail = 2,
Scim = 3,
Administrator = 4
}
}
15 changes: 15 additions & 0 deletions Core/Resgrid.Model/Identity/IdentityUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,21 @@ public IdentityUser(string userName) : this()
//[ProtoMember(15)]
public override int AccessFailedCount { get; set; }

/// <summary>
/// Monotonically increases whenever all credentials for this account must be invalidated.
/// Existing accounts begin at zero so installing the session schema does not sign them out.
/// </summary>
public long AuthenticationGeneration { get; set; }

/// <summary>
/// Credentials issued at or before this UTC instant are invalid. This is also the
/// compatibility boundary for cookies and tokens issued before session tracking existed.
/// </summary>
public DateTime? CredentialsValidAfterUtc { get; set; }

/// <summary>UTC timestamp of the latest account-wide authentication state change.</summary>
public DateTime? AuthenticationStateChangedOn { get; set; }

[System.ComponentModel.DataAnnotations.Schema.NotMapped]
public string UserId
{
Expand Down
6 changes: 4 additions & 2 deletions Core/Resgrid.Model/Providers/IEmailProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ public interface IEmailProvider
{
void Configure(object sender, string fromAddress);

Task<bool> SendWelcomeMail(string name, string departmentName, string userName, string password, string email, int departmentId);
Task<bool> SendPasswordResetMail(string name, string password, string userName, string email, string departmentName);
Task<bool> SendWelcomeMail(string name, string departmentName, string userName, string email, int departmentId);
Task<bool> SendPasswordRecoveryMail(string name, string email, string departmentName,
string resetUrl, string ipAddress, string userAgent, string requestedOn, bool isSsoManaged);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Sensitive data exposure in Core/Resgrid.Model/Providers/IEmailProvider.cs: passing raw ipAddress and userAgent propagates client-identifying values into downstream logging or templates, with related usage in Web/Resgrid.Web/Controllers/AccountController.cs:709, Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1245, Core/Resgrid.Model/UserSession.cs:39-40, Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:43, Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144, Web/Resgrid.Web/Areas/User/Controllers/AccountSecurityController.cs:209, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.edit.js:93, and Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.new.js:201, 243, and 408. Pass redacted or tokenized values such as ipAddressToken and userAgentToken, or use a structured context type that enforces masking.

Kody rule violation: Mask PII and secrets in logs

string resetUrl, string ipAddressToken, string userAgentToken, string requestedOn, bool isSsoManaged);
Prompt for LLM

File Core/Resgrid.Model/Providers/IEmailProvider.cs:

Line 12:

Sensitive data exposure in Core/Resgrid.Model/Providers/IEmailProvider.cs: passing raw ipAddress and userAgent propagates client-identifying values into downstream logging or templates, with related usage in Web/Resgrid.Web/Controllers/AccountController.cs:709, Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1245, Core/Resgrid.Model/UserSession.cs:39-40, Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:43, Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144, Web/Resgrid.Web/Areas/User/Controllers/AccountSecurityController.cs:209, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.edit.js:93, and Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.new.js:201, 243, and 408. Pass redacted or tokenized values such as ipAddressToken and userAgentToken, or use a structured context type that enforces masking.

Suggested Code:

			string resetUrl, string ipAddressToken, string userAgentToken, string requestedOn, bool isSsoManaged);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Privacy-by-default violation in Core/Resgrid.Model/Providers/IEmailProvider.cs: the method accepts raw personal data in ipAddress and userAgent without indicating minimization, with related usage in Web/Resgrid.Web/Controllers/AccountController.cs:709, Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1245, Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144, Core/Resgrid.Model/UserSession.cs:39-40, Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:43, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.edit.js:93, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.new.js:201, 243, and 408, and Web/Resgrid.Web/Areas/User/Controllers/AccountSecurityController.cs:209. Pass minimized forms such as ipAddressHash and userAgentToken, or encapsulate them in a type that enforces redaction and purpose metadata.

Kody rule violation: Redact PII in logs and metrics by default

string resetUrl, string ipAddressHash, string userAgentToken, string requestedOn, bool isSsoManaged);
Prompt for LLM

File Core/Resgrid.Model/Providers/IEmailProvider.cs:

Line 12:

Privacy-by-default violation in Core/Resgrid.Model/Providers/IEmailProvider.cs: the method accepts raw personal data in ipAddress and userAgent without indicating minimization, with related usage in Web/Resgrid.Web/Controllers/AccountController.cs:709, Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1245, Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144, Core/Resgrid.Model/UserSession.cs:39-40, Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:43, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.edit.js:93, Web/Resgrid.Web/wwwroot/js/app/internal/routes/resgrid.routes.new.js:201, 243, and 408, and Web/Resgrid.Web/Areas/User/Controllers/AccountSecurityController.cs:209. Pass minimized forms such as ipAddressHash and userAgentToken, or encapsulate them in a type that enforces redaction and purpose metadata.

Suggested Code:

			string resetUrl, string ipAddressHash, string userAgentToken, string requestedOn, bool isSsoManaged);

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Comment on lines +11 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Personal-data handling in Core/Resgrid.Model/Providers/IEmailProvider.cs exposes name, email, ipAddress, and userAgent in the SendPasswordRecoveryMail signature, increasing the chance that diagnostics capture raw PII across the listed call sites. Encapsulate these values in a typed request model that separates delivery fields from redacted telemetry fields and enforce redaction or hashing by default.

Kody rule violation: Redact PII in logs and metrics by default

Prompt for LLM

File Core/Resgrid.Model/Providers/IEmailProvider.cs:

Line 11 to 12:

Personal-data handling in Core/Resgrid.Model/Providers/IEmailProvider.cs exposes name, email, ipAddress, and userAgent in the SendPasswordRecoveryMail signature, increasing the chance that diagnostics capture raw PII across the listed call sites. Encapsulate these values in a typed request model that separates delivery fields from redacted telemetry fields and enforce redaction or hashing by default.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

kody code-review Kody Rules high

Sensitive data overexposure identified in Core/Resgrid.Model/Providers/IEmailProvider.cs, also at Core/Resgrid.Model/Services/IEmailService.cs:29-30, Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:51-51, Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:39-40, Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1181-1184, Web/Resgrid.Web.Services/Controllers/v4/ScimController.cs:212-214, Providers/Resgrid.Providers.Email/Template/PasswordChangedByAdministrator.html:25-26, and Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144-144, because the contract propagates raw ipAddress and userAgent through a broad mail-provider interface. Pass only minimum template data, or use redacted or tokenized metadata and ensure these values are never logged or persisted raw.

Kody rule violation: Do not log PHI; mask and drop sensitive fields

Prompt for LLM

File Core/Resgrid.Model/Providers/IEmailProvider.cs:

Line 12:

Sensitive data overexposure identified in `Core/Resgrid.Model/Providers/IEmailProvider.cs`, also at `Core/Resgrid.Model/Services/IEmailService.cs:29-30`, `Workers/Resgrid.Workers.Framework/Logic/AuditQueueLogic.cs:51-51`, `Providers/Resgrid.Providers.Email/Template/PasswordRecovery.html:39-40`, `Web/Resgrid.Web/Areas/User/Controllers/ProfileController.cs:1181-1184`, `Web/Resgrid.Web.Services/Controllers/v4/ScimController.cs:212-214`, `Providers/Resgrid.Providers.Email/Template/PasswordChangedByAdministrator.html:25-26`, and `Web/Resgrid.Web/wwwroot/js/app/internal/dispatch/resgrid.dispatch.addArchivedCall.js:144-144`, because the contract propagates raw `ipAddress` and `userAgent` through a broad mail-provider interface. Pass only minimum template data, or use redacted or tokenized metadata and ensure these values are never logged or persisted raw.

Talk to Kody by mentioning @kody

Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction.

Task<bool> SendPasswordChangedByAdministratorMail(string name, string userName, string email, string departmentName);
Task<bool> SendSignupMail(string name, string departmentName, string email);
Task<bool> SendMessageMail(string email, string subject, string messageSubject, string messageBody, string senderEmail, string senderName, string sentOn, int messageId);
Task<bool> SendCallMail(string email, string subject, string title, string priority, string natureOfCall, string mapPage,
Expand Down
19 changes: 18 additions & 1 deletion Core/Resgrid.Model/Providers/IGifProvider.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,35 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using ProtoBuf;

namespace Resgrid.Model.Providers
{
/// <summary>A GIF search hit returned to chat clients; urls point at the GIF CDN, never at Resgrid.</summary>
/// <summary>
/// A GIF search hit returned to chat clients; urls point at the GIF CDN, never at Resgrid.
/// Search results are cached, and the cache provider serializes with protobuf-net, so this type needs a
/// contract — without one every cache write throws and the per-query cache silently never populates.
/// </summary>
[ProtoContract]
public class GifSearchResult
{
[ProtoMember(1)]
public string Id { get; set; }

[ProtoMember(2)]
public string Title { get; set; }

/// <summary>Small preview/thumbnail url for the picker grid.</summary>
[ProtoMember(3)]
public string PreviewUrl { get; set; }

/// <summary>Full GIF url embedded in the message metadata.</summary>
[ProtoMember(4)]
public string GifUrl { get; set; }

[ProtoMember(5)]
public int Width { get; set; }

[ProtoMember(6)]
public int Height { get; set; }
}

Expand Down
14 changes: 0 additions & 14 deletions Core/Resgrid.Model/Repositories/IIdentityRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,6 @@ public interface IIdentityRepository
/// <returns>IdentityUser.</returns>
IdentityUser Update(IdentityUser user);

/// <summary>
/// Updates the username.
/// </summary>
/// <param name="oldUsername">The old username.</param>
/// <param name="newUsername">The new username.</param>
void UpdateUsername(string oldUsername, string newUsername);

/// <summary>
/// Adds the user to role.
/// </summary>
Expand Down Expand Up @@ -111,13 +104,6 @@ public interface IIdentityRepository
/// <returns>List&lt;UserGroupRole&gt;.</returns>
Task<List<UserGroupRole>> GetAllUsersGroupsAndRolesAsync(int departmentId, bool retrieveHidden, bool retrieveDisabled, bool retrieveDeleted);

/// <summary>
/// Updates the email.
/// </summary>
/// <param name="userId">The user identifier.</param>
/// <param name="newEmail">The new email.</param>
void UpdateEmail(string userId, string newEmail);

/// <summary>
/// Gets the user by user name asynchronous.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Resgrid.Model.Repositories
{
public interface IUserExternalIdentityLinksRepository : IRepository<UserExternalIdentityLink>
{
Task<UserExternalIdentityLink> GetActiveBySubjectAsync(string departmentSsoConfigId, string externalSubject);
Task<UserExternalIdentityLink> GetActiveByUserAndConfigAsync(string userId, string departmentSsoConfigId);
Task<IReadOnlyList<UserExternalIdentityLink>> GetActiveByUserAsync(string userId);
}
}
Loading
Loading