Skip to content

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Sep 18, 2025

Summary

Implements comprehensive password management functionality for user profiles with OWASP-compliant validation.

Changes

  • Password Change Form: Current/new/confirm password fields with show/hide toggles
  • OWASP Validation: 8+ characters, 3 of 4 character types (lowercase, uppercase, numbers, special)
  • Real-time Strength Indicator: Visual feedback with dynamic requirements checklist
  • Password Reset: Simple CTA button (backend determines email from current user)
  • 2FA Integration: Status display with external setup/management link
  • Signal-based Reactivity: Proper reactive updates for zoneless change detection
  • Comprehensive Testing: data-testid attributes for all interactive elements

Technical Implementation

  • Follows existing component patterns from profile-edit and email components
  • Uses FormBuilder with custom validators for password strength and matching
  • Signal-based state management for real-time updates
  • Toast notifications for user feedback
  • Proper error handling and validation

Testing

  • All interactive elements have data-testid attributes following the pattern: password-[section]-[element]
  • Password strength calculator updates in real-time as user types
  • Password match validation works automatically via form-level validator
  • Form validation prevents submission with invalid data
  • Loading states and error handling tested

JIRA

LFXV2-528

Checklist

  • Code follows project conventions and patterns
  • Lint passes without errors
  • Build succeeds without warnings
  • Password strength validation works in real-time
  • Password match validation functions properly
  • Component follows existing UI/UX patterns
  • All form fields have proper validation
  • Error handling and user feedback implemented
  • Data-testid attributes added for testing

- Add password change functionality with current/new/confirm fields
- Implement OWASP password validation (8+ chars, 3 of 4 character types)
- Add real-time password strength indicator with visual feedback
- Implement password reset email functionality for current user
- Add 2FA status display and external setup link
- Use signal-based state management for reactive updates
- Add comprehensive form validation and error handling
- Include data-testid attributes for all interactive elements

LFXV2-528

Signed-off-by: Asitha de Silva <asithade@gmail.com>
@asithade asithade requested a review from jordane as a code owner September 18, 2025 18:36
Copilot AI review requested due to automatic review settings September 18, 2025 18:36
@coderabbitai
Copy link

coderabbitai bot commented Sep 18, 2025

Walkthrough

Refactors profile edit form to use a shared control-touch utility. Replaces the profile password placeholder with a fully functional, signal-driven password management UI and logic. Adds password/2FA API methods to UserService. Introduces shared interfaces for password requests, strength, and two-factor settings.

Changes

Cohort / File(s) Summary of changes
Profile edit form touch handling
apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts
Switched invalid-form handling to use shared markFormControlsAsTouched; removed internal markFormGroupTouched helper; updated imports.
Password UI overhaul (template)
apps/lfx-one/src/app/modules/profile/password/profile-password.component.html
Replaced template with full Change Password form, password strength/requirements, visibility toggles, account recovery action, and Two-Factor Authentication section with conditional states.
Password feature logic (component)
apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts
Rewrote component as standalone, signal-driven. Added reactive form with validators, password strength computation, change/reset handlers, visibility toggles, 2FA loading via signals, and message feedback. Implements OnInit; injects services.
User service: password and 2FA APIs
apps/lfx-one/src/app/shared/services/user.service.ts
Added changePassword, sendPasswordResetEmail, and getTwoFactorSettings methods calling respective API endpoints; updated imports and docs.
Shared interfaces for auth/profile
packages/shared/src/interfaces/user-profile.interface.ts
Added ChangePasswordRequest, PasswordResetRequest, PasswordStrength, and TwoFactorSettings interfaces; no modifications to existing types.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant C as ProfilePasswordComponent
  participant US as UserService
  participant API as Backend API
  participant MS as MessageService

  U->>C: Click "Change Password"
  C->>C: Validate form (match/strength)
  alt Invalid
    C->>U: Show form validation errors
  else Valid
    C->>US: changePassword({current,new})
    US->>API: POST /api/profile/change-password
    API-->>US: { message } or error
    US-->>C: Success or error
    opt Finalize
      C->>C: Reset loading state
    end
    alt Success
      C->>MS: showSuccess(message)
      C->>C: Reset/untouch form
    else Error
      C->>MS: showError(error)
    end
  end
Loading
sequenceDiagram
  autonumber
  actor U as User
  participant C as ProfilePasswordComponent
  participant US as UserService
  participant API as Backend API
  participant MS as MessageService

  Note over C: Two-Factor settings load (on init or refresh)
  C->>US: getTwoFactorSettings()
  US->>API: GET /api/profile/2fa-settings
  API-->>US: TwoFactorSettings or error
  US-->>C: TwoFactorSettings|null
  C->>C: Update signals for UI state

  U->>C: Click "Send Reset Link"
  C->>US: sendPasswordResetEmail()
  US->>API: POST /api/profile/reset-password
  API-->>US: { message } or error
  US-->>C: Response
  alt Success
    C->>MS: showSuccess(message)
  else Error
    C->>MS: showError(error)
  end
  C->>C: Reset sending state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The PR includes an out-of-scope refactor: apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts was updated to use a newly exported markFormControlsAsTouched utility and the shared package now exposes markFormControlsAsTouched(formGroup: FormGroup); these changes are not documented in LFXV2-528 and introduce a shared/public API change outside the ticket's stated objectives. Recommend extracting the utility/API change into a separate refactor PR or add a clear justification in this PR description and ensure the shared API addition has tests and release/API notes before merging.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The provided title "feat(profile): implement password management with OWASP validation" is concise, highlights the primary change (password management) and the important constraint (OWASP validation), and accurately reflects the main purpose of the changeset.
Linked Issues Check ✅ Passed All coding-related objectives from LFXV2-528 appear implemented: the shared interfaces were added (packages/shared/src/interfaces/user-profile.interface.ts), UserService gained changePassword/sendPasswordResetEmail/getTwoFactorSettings (apps/lfx-one/src/app/shared/services/user.service.ts), and the profile password component and template (apps/lfx-one/.../profile-password.component.ts and .html) include current/new/confirm fields, OWASP validators, real-time strength indicator, match validation, data-testid attributes, and 2FA state handling as required by the linked issue.
Description Check ✅ Passed The PR description clearly summarizes the feature, technical approach, testing notes, and links the JIRA ticket LFXV2-528, and its contents align with the changed files and functionality described in the patch (password form, OWASP rules, strength indicator, reset CTA, 2FA, interfaces and service methods).
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/LFXV2-528

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

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 implements comprehensive password management functionality for user profiles with OWASP-compliant validation, adding password change forms, strength indicators, reset functionality, and 2FA integration.

Key Changes

  • Password Management Interface: Added comprehensive TypeScript interfaces for password operations and 2FA settings
  • Service Layer Enhancement: Extended UserService with password change, reset, and 2FA endpoints
  • Complete UI Implementation: Built full password management component with real-time validation, strength indicators, and 2FA status display

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/shared/src/interfaces/user-profile.interface.ts Adds TypeScript interfaces for password operations and 2FA settings
apps/lfx-one/src/app/shared/services/user.service.ts Extends UserService with password management and 2FA API methods
apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts Implements complete password management component with validation and state management
apps/lfx-one/src/app/modules/profile/password/profile-password.component.html Creates comprehensive UI for password change, reset, and 2FA management
apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts Refactors to use shared utility function for form validation

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (8)
packages/shared/src/interfaces/user-profile.interface.ts (3)

132-134: PasswordResetRequest doesn’t match current API usage.

Service sends an empty body. Make email optional or remove this interface to avoid drift.

Apply one of these diffs:

-export interface PasswordResetRequest {
-  email: string;
-}
+// Not used by current API (server infers email). Keep optional for future use.
+export interface PasswordResetRequest {
+  email?: string;
+}

—or—

-/**
- * Request to send password reset email
- */
-export interface PasswordResetRequest {
-  email: string;
-}
+// Removed: API infers user email; no payload needed.

155-160: backup_codes_count should be optional to match template checks.

Template guards with !== undefined. Mark the field optional to reflect API variability.

-export interface TwoFactorSettings {
+export interface TwoFactorSettings {
   enabled: boolean;
   method: 'app' | 'sms' | 'email' | null;
-  backup_codes_count: number;
+  backup_codes_count?: number;
   last_used: string | null;
 }

141-149: Prefer enums over string literal unions for shared contracts.

Improves discoverability and refactor safety across apps.

+export enum PasswordStrengthLabel {
+  weak = 'weak',
+  fair = 'fair',
+  good = 'good',
+  strong = 'strong',
+}
+
+export enum TwoFactorMethod {
+  app = 'app',
+  sms = 'sms',
+  email = 'email',
+}
 
 export interface PasswordStrength {
   score: number; // 0-4 (weak to strong)
-  label: 'weak' | 'fair' | 'good' | 'strong';
+  label: PasswordStrengthLabel;
   requirements: {
     minLength: boolean;
     hasLowercase: boolean;
     hasUppercase: boolean;
     hasNumbers: boolean;
     hasSpecialChars: boolean;
     meetsCriteria: boolean; // true if 3 of 4 character types are present
   };
 }
 
 export interface TwoFactorSettings {
   enabled: boolean;
-  method: 'app' | 'sms' | 'email' | null;
+  method: TwoFactorMethod | null;
   backup_codes_count?: number;
   last_used: string | null;
 }

Please confirm no downstream code relies on string unions before we switch to enums.

Also applies to: 156-158

apps/lfx-one/src/app/shared/services/user.service.ts (1)

41-43: Consistency nit: favor take(1) for one-shot HTTP calls.

Other methods use it; align getCurrentUserProfile (and optionally getUserEmails) for consistency.

-  public getCurrentUserProfile(): Observable<CombinedProfile> {
-    return this.http.get<CombinedProfile>('/api/profile');
-  }
+  public getCurrentUserProfile(): Observable<CombinedProfile> {
+    return this.http.get<CombinedProfile>('/api/profile').pipe(take(1));
+  }

Also consider giving updateProfileDetails a concrete return type instead of any.

Can the backend return the updated ProfileDetails? If yes, we can type it.

Also applies to: 64-66, 55-57

apps/lfx-one/src/app/modules/profile/password/profile-password.component.html (1)

48-49: [class] overrides static classes; use [ngClass] to merge.

Current pattern risks dropping text-gray-400 hover:text-gray-600.

-  <i [class]="showCurrentPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600"></i>
+  <i [ngClass]="showCurrentPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600" aria-hidden="true"></i>

-  <i [class]="showNewPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600"></i>
+  <i [ngClass]="showNewPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600" aria-hidden="true"></i>

-  <i [class]="showConfirmPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600"></i>
+  <i [ngClass]="showConfirmPassword() ? 'fa-light fa-eye-slash' : 'fa-light fa-eye'" class="text-gray-400 hover:text-gray-600" aria-hidden="true"></i>

Also applies to: 73-74, 157-158

apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts (3)

100-137: Success/error flows are clean; small UX tweak.

After success, consider focusing the first field or showing a brief toast duration to improve feedback.


170-174: Prevent reverse‑tabnabbing when opening external links.

Add noopener,noreferrer.

-    window.open('https://docs.example.com/2fa-setup', '_blank');
+    window.open('https://docs.example.com/2fa-setup', '_blank', 'noopener,noreferrer');

195-223: UI text vs. regex discrepancy for “special characters.”

Regex allows a wider set than the checklist text. Align them to avoid confusion.

Option A (update copy): “Special character (e.g., !@#$%^&*)”.
Option B (restrict regex to displayed set):

-      hasSpecialChars: /[!@#$%^&*(),.?":{}|<>]/.test(password),
+      hasSpecialChars: /[!@#$%^&*]/.test(password),

Confirm which set the policy intends to enforce.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4392e5c and 2d988a5.

📒 Files selected for processing (5)
  • apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts (2 hunks)
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts (1 hunks)
  • apps/lfx-one/src/app/shared/services/user.service.ts (2 hunks)
  • packages/shared/src/interfaces/user-profile.interface.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
packages/shared/src/interfaces/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Place all TypeScript interfaces in the shared package at packages/shared/src/interfaces

Files:

  • packages/shared/src/interfaces/user-profile.interface.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript interfaces instead of union types for better maintainability
When defining PrimeNG-related types, reference the official PrimeNG component interfaces

Files:

  • packages/shared/src/interfaces/user-profile.interface.ts
  • apps/lfx-one/src/app/shared/services/user.service.ts
  • apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts
**/*.{ts,tsx,js,jsx,mjs,cjs,html,css,scss}

📄 CodeRabbit inference engine (CLAUDE.md)

Include required license headers on all source files

Files:

  • packages/shared/src/interfaces/user-profile.interface.ts
  • apps/lfx-one/src/app/shared/services/user.service.ts
  • apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.html
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not nest ternary expressions

Files:

  • packages/shared/src/interfaces/user-profile.interface.ts
  • apps/lfx-one/src/app/shared/services/user.service.ts
  • apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts
  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts
apps/lfx-one/src/**/*.html

📄 CodeRabbit inference engine (CLAUDE.md)

apps/lfx-one/src/**/*.html: Always add data-testid attributes when creating new Angular components for reliable test targeting
Use data-testid naming convention [section]-[component]-[element]

Files:

  • apps/lfx-one/src/app/modules/profile/password/profile-password.component.html
🧬 Code graph analysis (3)
apps/lfx-one/src/app/shared/services/user.service.ts (1)
packages/shared/src/interfaces/user-profile.interface.ts (2)
  • ChangePasswordRequest (124-127)
  • TwoFactorSettings (155-160)
apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts (1)
packages/shared/src/utils/form.utils.ts (1)
  • markFormControlsAsTouched (16-19)
apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts (3)
apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts (1)
  • Component (21-243)
packages/shared/src/interfaces/user-profile.interface.ts (3)
  • TwoFactorSettings (155-160)
  • ChangePasswordRequest (124-127)
  • PasswordStrength (139-150)
packages/shared/src/utils/form.utils.ts (1)
  • markFormControlsAsTouched (16-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Code Quality Checks
🔇 Additional comments (5)
apps/lfx-one/src/app/modules/profile/edit/profile-edit.component.ts (2)

8-8: Good move: centralize touched-state logic via shared util.

Importing markFormControlsAsTouched from shared keeps behavior consistent across forms.


120-124: Form invalid handling is correct.

Using the shared markFormControlsAsTouched(this.profileForm) before early‑return is aligned with our patterns.

apps/lfx-one/src/app/shared/services/user.service.ts (1)

104-125: Password/2FA endpoints look good and typed.

Methods are concise, use POST/GET appropriately, and take(1) ensures completion.

apps/lfx-one/src/app/modules/profile/password/profile-password.component.html (1)

21-181: Template structure and test hooks are solid.

Comprehensive data-testid coverage, a11y labels on toggles, and clear state handling.

Also applies to: 186-216, 220-316

apps/lfx-one/src/app/modules/profile/password/profile-password.component.ts (1)

225-256: Validators correctly enforce OWASP basics (min 8 + 3 of 4).

Group-level match validator and field-level strength validator are succinct and readable.

@github-actions
Copy link

✅ E2E Tests Passed

Browser: chromium
Status: passed

All E2E tests passed successfully.

Test Configuration

@asithade asithade merged commit d34d9f0 into main Sep 18, 2025
9 checks passed
@asithade asithade deleted the feat/LFXV2-528 branch September 18, 2025 19:28
@github-actions
Copy link

🧹 Deployment Removed

The deployment for PR #95 has been removed.

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.

3 participants