Skip to content

Conversation

@ljonesfl
Copy link
Member

@ljonesfl ljonesfl commented Jan 15, 2026

Note

Introduces an interactive CLI for resetting user passwords with validation against configurable password policies.

  • Adds cms:user:reset-password command (ResetPasswordCommand) and registers it in CLI Provider
  • Validates passwords via PasswordHasher (policy loaded from auth.passwords settings); clears lockouts on success
  • Comprehensive unit tests for success, failure paths, options, and policy loading
  • Minor UI changes: revamped 404 view heading; footer branding updated to “NeuronCMS”
  • Config/infra: system.base_path set to . in config/neuron.yaml; .gitignore now ignores runtime storage/ paths

Written by Cursor Bugbot for commit 5e5dc98. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Add CLI command to reset user passwords via interactive prompts with policy validation.
  • UI

    • Redesigned 404 page to a clearer two-line heading.
  • Chores

    • Footer branding updated from NeuronPHP to NeuronCMS.
    • Adjusted system base path and added runtime artifact ignore patterns.
  • Tests

    • Added comprehensive tests covering the password reset command.
  • Docs

    • Release notes updated for the new CLI command.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds a new interactive CLI command to reset user passwords (with provider registration and tests); updates config base_path, footer branding to "NeuronCMS", 404 view markup, .gitignore runtime entries, and the changelog.

Changes

Cohort / File(s) Summary
Ignore Patterns
/.gitignore
Added var/storage/ and var/resources/storage/ entries to ignore runtime artifacts.
Configuration
config/neuron.yaml
Changed system.base_path from "resources" to ".".
View Templates
resources/views/layouts/default.php, resources/views/layouts/member.php
Footer attribution text changed from "NeuronPHP" to "NeuronCMS".
Error Page
resources/views/http_codes/404.php
Adjusted markup: added <h1 class="text-secondary">404</h1> and a separate <h2> for the original message; updated row/div structure.
CLI Command
src/Cms/Cli/Commands/User/ResetPasswordCommand.php
New ResetPasswordCommand (cms:user:reset-password): loads settings, resolves user by username/email, confirms action, validates new password against policy, hashes and updates user record.
CLI Registration
src/Cms/Cli/Provider.php
Registered cms:user:reset-password mapped to the new command in the CLI provider.
Unit Tests
tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php
New comprehensive tests covering configuration, interactive flows (username/email/options), policy validation failures, and repository interactions.
Changelog
versionlog.md
Added entry noting the new cms:user:reset-password CLI command.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI User
    participant Cmd as ResetPasswordCommand
    participant Reg as Registry
    participant Repo as DatabaseUserRepository
    participant Hasher as PasswordHasher
    participant DB as Database

    CLI->>Cmd: Execute command
    Cmd->>Reg: Load Settings
    Reg-->>Cmd: Settings

    Cmd->>CLI: Prompt for identifier or read option
    CLI-->>Cmd: Identifier

    Cmd->>Repo: findByUsername/findByEmail(identifier)
    Repo->>DB: Query user
    DB-->>Repo: User record
    Repo-->>Cmd: User object

    Cmd->>CLI: Show user info & ask confirm
    CLI-->>Cmd: Confirmation

    alt confirmed
        Cmd->>CLI: Prompt new password & confirmation
        CLI-->>Cmd: Passwords
        Cmd->>Hasher: Validate & hash password
        Hasher-->>Cmd: Hashed password
        Cmd->>Repo: update(user with new hash & metadata)
        Repo->>DB: Persist changes
        DB-->>Repo: Success
        Repo-->>Cmd: Updated user
        Cmd->>CLI: Success message
    else cancelled
        Cmd->>CLI: Cancelled message
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped a command into the night,
Prompted names, then hashed them right,
Tests chased prompts and edge-case frights,
Footers swapped and 404s took flight,
NeuronCMS — I thumped with delight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feature/cli password reset' directly corresponds to the main change: adding a new interactive CLI command for password reset with full implementation, tests, and registration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (1)

45-45: Unused parameter is inherited from interface.

The $parameters argument flagged by static analysis is part of the Command interface signature and cannot be removed. If the codebase uses PHP 8+, you can suppress the warning by prefixing with underscore.

♻️ Suggested change
-	public function execute( array $parameters = [] ): int
+	public function execute( array $_parameters = [] ): int

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e2daa3 and 5e5dc98.

📒 Files selected for processing (1)
  • src/Cms/Cli/Commands/User/ResetPasswordCommand.php
🧰 Additional context used
🪛 PHPMD (2.15.0)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php

45-45: Avoid unused parameters such as '$parameters'. (undefined)

(UnusedFormalParameter)

⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (sqlite)
🔇 Additional comments (6)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (6)

58-104: LGTM!

Password policy loading from settings is well-implemented with appropriate null checks and exception handling that falls back to defaults gracefully.


133-152: LGTM!

User lookup logic correctly handles both username and email cases with appropriate error messages. The explicit "not found" messages are acceptable for an admin CLI tool where feedback is needed.


154-168: LGTM!

Good UX to display user details before confirmation, and the explicit 'yes' requirement with case-insensitive matching is appropriate for a destructive operation.


170-194: LGTM!

Password input now correctly uses secret() for masked input and validates against the configured policy with helpful error messages. Password confirmation prevents typos.


196-223: LGTM!

The password update logic now properly checks the return value from repository->update() and handles failure appropriately. Clearing the lockout state is a thoughtful addition for locked-out users.


231-251: LGTM!

The protected visibility for testability is well-documented and appropriate. Error handling provides useful feedback for both configuration and database connection issues.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@codecov
Copy link

codecov bot commented Jan 15, 2026

Codecov Report

❌ Patch coverage is 84.46602% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Cms/Cli/Commands/User/ResetPasswordCommand.php 87.00% 13 Missing ⚠️
src/Cms/Cli/Provider.php 0.00% 3 Missing ⚠️
Flag Coverage Δ Complexity Δ
cms 73.79% <84.46%> (+0.26%) 2126.00 <29.00> (+29.00)

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

Files with missing lines Coverage Δ Complexity Δ
src/Cms/Cli/Provider.php 0.00% <0.00%> (ø) 1.00 <0.00> (ø)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php 87.00% <87.00%> (ø) 29.00 <29.00> (?)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@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: 2

🤖 Fix all issues with AI agents
In `@resources/views/http_codes/404.php`:
- Around line 3-8: Both headings currently use the centered class which applies
margin-top:100px twice; remove the redundant top margin by keeping the centered
class only on the first heading or move the class to the parent container (the
div with class "row") so the margin-top is applied once; update the markup to
remove the centered class from the second heading (h2) or create/assign a single
container-level class (e.g., on the "row" div) that provides the margin, and
adjust CSS accordingly to ensure only one margin-top:100px is applied.

In `@versionlog.md`:
- Line 2: Update the changelog entry to use the actual CLI command name: replace
the documented cms:user:password-reset with cms:user:reset-password in
versionlog.md so the entry matches the registered command.
🧹 Nitpick comments (2)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (2)

124-131: Hardcoded minimum length may conflict with PasswordHasher configuration.

The hardcoded 8-character check on line 127 may be inconsistent with PasswordHasher::$_minLength. If the hasher's minimum length is configured differently, users could see confusing error messages (e.g., passing the 8-char check but failing meetsRequirements()).

Consider using the hasher's configured minimum length instead:

Suggested fix
 		// Get new password
-		$password = $this->prompt( "\nEnter new password (min 8 characters): " );
-
-		if( strlen( $password ) < 8 )
-		{
-			$this->output->error( "Password must be at least 8 characters long!" );
-			return 1;
-		}
+		$password = $this->prompt( "\nEnter new password: " );

 		// Validate password
 		if( !$hasher->meetsRequirements( $password ) )

This removes the redundant check since meetsRequirements() already validates minimum length and provides detailed error messages via getValidationErrors().


159-166: Redundant setUpdatedAt call.

Line 160 sets updatedAt, but repository->update() on line 166 also sets it (see DatabaseUserRepository::update() line 108). The second call overwrites the first, making line 160 redundant.

Suggested fix
 		try
 		{
 			$user->setPasswordHash( $hasher->hash( $password ) );
-			$user->setUpdatedAt( new \DateTimeImmutable() );

 			// Clear any lockout
 			$user->setFailedLoginAttempts( 0 );
 			$user->setLockedUntil( null );

 			$repository->update( $user );
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f08630 and 6818a25.

📒 Files selected for processing (9)
  • .gitignore
  • config/neuron.yaml
  • resources/views/http_codes/404.php
  • resources/views/layouts/default.php
  • resources/views/layouts/member.php
  • src/Cms/Cli/Commands/User/ResetPasswordCommand.php
  • src/Cms/Cli/Provider.php
  • tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php
  • versionlog.md
🧰 Additional context used
🧬 Code graph analysis (2)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (3)
src/Cms/Repositories/DatabaseUserRepository.php (1)
  • DatabaseUserRepository (19-243)
src/Cms/Auth/PasswordHasher.php (4)
  • PasswordHasher (13-204)
  • meetsRequirements (53-86)
  • getValidationErrors (93-123)
  • hash (24-30)
src/Cms/Models/User.php (4)
  • getUsername (92-95)
  • getRole (143-146)
  • setPasswordHash (134-138)
  • setFailedLoginAttempts (301-305)
tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (3)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (4)
  • ResetPasswordCommand (14-206)
  • getName (20-23)
  • getDescription (28-31)
  • execute (45-178)
src/Cms/Repositories/DatabaseUserRepository.php (1)
  • DatabaseUserRepository (19-243)
src/Cms/Models/User.php (4)
  • setUsername (100-104)
  • setRole (151-155)
  • setPasswordHash (134-138)
  • getPasswordHash (126-129)
🪛 PHPMD (2.15.0)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php

45-45: Avoid unused parameters such as '$parameters'. (undefined)

(UnusedFormalParameter)

⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (sqlite)
🔇 Additional comments (8)
resources/views/layouts/member.php (1)

66-66: LGTM!

Branding update from "NeuronPHP" to "NeuronCMS" is consistent with the related change in the default layout.

resources/views/layouts/default.php (1)

54-54: LGTM!

Branding update aligns with the member layout change.

.gitignore (1)

39-42: LGTM!

Adding storage/ and resources/storage/ to gitignore is appropriate for excluding runtime artifacts like logs, cache, and temporary files from version control.

config/neuron.yaml (1)

6-6: Verify the implementation of base_path configuration wiring.

The change from base_path: resources to base_path: . is significant, but the codebase shows a potential disconnect: EmailVerifierFactory, PasswordResetterFactory, and PasswordResetInitializer retrieve RegistryKeys::BASE_PATH from the Registry with fallback to getcwd(), yet I cannot confirm how the configuration value from neuron.yaml is wired to populate RegistryKeys::BASE_PATH.

Confirm that the system.base_path configuration is properly transferred to the Registry during application boot, and that this change doesn't cause path resolution issues in email verification, password reset, and other base-path-dependent features.

src/Cms/Cli/Provider.php (1)

48-51: LGTM!

The new command registration follows the established pattern for user management commands and maintains naming consistency with the cms:user:* namespace.

tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (1)

1-461: Comprehensive test coverage for the new command.

The test file provides thorough coverage including:

  • Command metadata verification
  • Missing configuration error handling
  • Success paths for both username and email lookups
  • Failure cases (user not found, declined, short password, mismatch)
  • CLI option-based flows

The use of mocking and TestInputReader effectively isolates the command logic for unit testing.

src/Cms/Cli/Commands/User/ResetPasswordCommand.php (2)

45-45: Unused parameter $parameters is expected.

The $parameters parameter is part of the parent Command::execute() signature and must be present for interface compliance, even if unused in this implementation. The static analysis warning can be safely ignored.


180-205: LGTM!

The getUserRepository() method provides clean separation for testability and includes proper error handling with descriptive messages for both missing configuration and database connection failures.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@src/Cms/Cli/Commands/User/ResetPasswordCommand.php`:
- Line 125: The password prompts in ResetPasswordCommand are using
$this->prompt(), exposing input; replace both uses (the new password assignment
to $password and the confirmation assignment to $passwordConfirm) with
$this->secret() so input is masked. Locate the two instances in the
ResetPasswordCommand where "$this->prompt( ... Enter new password ... )" and the
confirmation prompt are called and change them to "$this->secret(...)" while
preserving the prompt text and validation logic.
♻️ Duplicate comments (1)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (1)

166-172: Return value is now properly checked.

The update() return value is checked and appropriate error handling is in place. This addresses the previous review feedback.

🧹 Nitpick comments (5)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (1)

45-45: Consider suppressing the unused parameter warning.

The $parameters parameter is unused but required by the parent Command interface. Suppress the static analysis warning with a docblock annotation.

Suggested fix
 	/**
 	 * Execute the command
+	 *
+	 * `@SuppressWarnings`(PHPMD.UnusedFormalParameter)
 	 */
 	public function execute( array $parameters = [] ): int
tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (4)

18-18: Add type hint for $root property.

The $root property is untyped. Consider adding the type hint for consistency.

Suggested fix
-	private $root;
+	private \org\bovigo\vfs\vfsStreamDirectory $root;

26-40: Virtual filesystem configuration appears unused.

The vfsStream setup creates a config/neuron.yaml file, but all tests mock getUserRepository() directly, bypassing the actual configuration loading. Consider removing this setup if it's not needed, or add a test that exercises the real configuration path.


72-72: Use the imported class reference.

Registry is already imported on line 12, so the fully qualified namespace is unnecessary.

Suggested fix
-		\Neuron\Patterns\Registry::getInstance()->reset();
+		Registry::getInstance()->reset();

215-242: Consider adding test for email not found scenario.

This test covers username not found, but there's no corresponding test for when findByEmail returns null. This would ensure the email lookup error path is also covered.

Suggested test case
public function testExecuteFailsWhenUserNotFoundByEmail(): void
{
    $this->inputReader->addResponse('nonexistent@example.com');

    $repository = $this->createMock(DatabaseUserRepository::class);
    $repository->expects($this->once())
        ->method('findByEmail')
        ->with('nonexistent@example.com')
        ->willReturn(null);

    $settings = $this->createMock(SettingManager::class);
    Registry::getInstance()->set('Settings', $settings);

    $command = $this->getMockBuilder(ResetPasswordCommand::class)
        ->onlyMethods(['getUserRepository'])
        ->getMock();
    $command->expects($this->once())
        ->method('getUserRepository')
        ->willReturn($repository);

    $command->setInput($this->input);
    $command->setOutput($this->output);
    $command->setInputReader($this->inputReader);

    $exitCode = $command->execute();

    $this->assertEquals(1, $exitCode);
}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6818a25 and 495b3ac.

📒 Files selected for processing (3)
  • src/Cms/Cli/Commands/User/ResetPasswordCommand.php
  • tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php
  • versionlog.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • versionlog.md
🧰 Additional context used
🧬 Code graph analysis (1)
tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (3)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (4)
  • ResetPasswordCommand (14-212)
  • getName (20-23)
  • getDescription (28-31)
  • execute (45-184)
src/Cms/Repositories/DatabaseUserRepository.php (1)
  • DatabaseUserRepository (19-243)
src/Cms/Models/User.php (4)
  • setUsername (100-104)
  • setRole (151-155)
  • setPasswordHash (134-138)
  • getPasswordHash (126-129)
🪛 PHPMD (2.15.0)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php

45-45: Avoid unused parameters such as '$parameters'. (undefined)

(UnusedFormalParameter)

⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
🔇 Additional comments (10)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (3)

1-31: LGTM!

Namespace, imports, and command metadata methods are correctly implemented.


33-40: LGTM!

Command options are properly configured with appropriate short flags.


186-211: LGTM!

The repository getter has proper error handling for both missing settings and database connection failures, with clear error messages to guide debugging.

tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (7)

54-67: LGTM!

Good test of command metadata. Using reflection to test the protected configure() method is appropriate.


98-160: LGTM!

Excellent test coverage for the success path. The callback assertion verifying the password hash changed is a good approach, and validating the prompt history ensures the full user interaction flow is exercised.


162-213: LGTM!

Good coverage of the email-based lookup flow, complementing the username-based test.


244-283: LGTM!

Good test verifying cancellation returns exit code 0 and doesn't call update().


285-360: LGTM!

Comprehensive failure scenario coverage for password validation errors.


362-460: LGTM!

Good tests verifying that CLI options bypass interactive prompts. The prompt count assertions (3 instead of 4) effectively validate this behavior.


462-505: LGTM!

This test validates the fix for the previously unchecked update() return value. Good coverage of the database failure scenario.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@src/Cms/Cli/Commands/User/ResetPasswordCommand.php`:
- Line 58: Load password policy from the registry before validating: fetch
settings via Registry::getInstance()->get(RegistryKeys::SETTINGS), instantiate
PasswordHasher as currently done, then (inside a try/catch) read configured keys
like password.min_length, password.require_uppercase,
password.require_lowercase, password.require_numbers,
password.require_special_chars and call the corresponding setters on
PasswordHasher (e.g., setMinLength, setRequireUppercase, setRequireLowercase,
setRequireNumbers, setRequireSpecialChars) so that meetsRequirements() used
later honors site policy; fall back to defaults on any exception.
🧹 Nitpick comments (1)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (1)

127-131: Redundant minimum length check.

This hardcoded 8-character check is redundant since meetsRequirements() (line 134) already validates password length via PasswordHasher's configured minLength. Additionally, if the hasher is configured with a different minimum (e.g., 12 characters), this check would allow an 8-character password to pass here but then fail at meetsRequirements(), showing confusing error messages.

Consider removing this block and relying solely on meetsRequirements():

♻️ Suggested simplification
 		// Get new password
-		$password = $this->secret( "\nEnter new password (min 8 characters): " );
-
-		if( strlen( $password ) < 8 )
-		{
-			$this->output->error( "Password must be at least 8 characters long!" );
-			return 1;
-		}
+		$password = $this->secret( "\nEnter new password: " );
 
 		// Validate password
 		if( !$hasher->meetsRequirements( $password ) )
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 495b3ac and 5fee55e.

📒 Files selected for processing (1)
  • src/Cms/Cli/Commands/User/ResetPasswordCommand.php
🧰 Additional context used
🧬 Code graph analysis (1)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (3)
src/Cms/Repositories/DatabaseUserRepository.php (1)
  • DatabaseUserRepository (19-243)
src/Cms/Auth/PasswordHasher.php (4)
  • PasswordHasher (13-204)
  • meetsRequirements (53-86)
  • getValidationErrors (93-123)
  • hash (24-30)
src/Cms/Models/User.php (4)
  • getUsername (92-95)
  • getRole (143-146)
  • setPasswordHash (134-138)
  • setFailedLoginAttempts (301-305)
🪛 PHPMD (2.15.0)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php

45-45: Avoid unused parameters such as '$parameters'. (undefined)

(UnusedFormalParameter)

⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (postgres)
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
🔇 Additional comments (5)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (5)

1-10: LGTM!

Namespace and imports are clean and all imported classes are utilized within the command.


14-31: LGTM!

Command name follows the established cms:user:* pattern and the description is clear.


33-40: LGTM!

Options are well-configured with both long and short forms, providing flexibility for user lookup.


156-183: LGTM!

The update logic properly:

  • Hashes the password before storage
  • Updates the timestamp
  • Clears lockout state (appropriate for admin password reset)
  • Checks the repository's return value and handles failures
  • Catches and reports exceptions

The previous concern about unchecked return value from repository->update() has been addressed.


186-211: LGTM!

Well-designed factory method with:

  • Protected visibility for test mockability (as documented)
  • Proper null-safety with informative error messages
  • Exception handling for database connection failures

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@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: 1

🤖 Fix all issues with AI agents
In `@src/Cms/Cli/Commands/User/ResetPasswordCommand.php`:
- Around line 171-177: Remove the hardcoded strlen check and the "at least 8
characters" prompt text in ResetPasswordCommand so validation and messaging come
from the configured password policy; call the same password policy used by
meetsRequirements() to read the configured minimum length for the prompt (or
omit the numeric hint) and let meetsRequirements() perform all validation,
replacing the current error path that calls $this->output->error("Password must
be at least 8 characters long!")/return 1 with the policy-based error reporting.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5fee55e and 1e2daa3.

📒 Files selected for processing (2)
  • src/Cms/Cli/Commands/User/ResetPasswordCommand.php
  • tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php
🧰 Additional context used
🧬 Code graph analysis (2)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (3)
src/Cms/Repositories/DatabaseUserRepository.php (1)
  • DatabaseUserRepository (19-243)
src/Cms/Auth/PasswordHasher.php (9)
  • PasswordHasher (13-204)
  • setMinLength (128-132)
  • setRequireUppercase (137-141)
  • setRequireLowercase (146-150)
  • setRequireNumbers (155-159)
  • setRequireSpecialChars (164-168)
  • meetsRequirements (53-86)
  • getValidationErrors (93-123)
  • hash (24-30)
src/Cms/Models/User.php (4)
  • getUsername (92-95)
  • getRole (143-146)
  • setPasswordHash (134-138)
  • setFailedLoginAttempts (301-305)
tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (1)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (4)
  • ResetPasswordCommand (14-258)
  • getName (20-23)
  • getDescription (28-31)
  • execute (45-230)
🪛 PHPMD (2.15.0)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php

45-45: Avoid unused parameters such as '$parameters'. (undefined)

(UnusedFormalParameter)

⏰ 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). (4)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: build-test (sqlite)
  • GitHub Check: build-test (mysql)
  • GitHub Check: build-test (postgres)
🔇 Additional comments (18)
src/Cms/Cli/Commands/User/ResetPasswordCommand.php (5)

1-40: LGTM - Command metadata and configuration are well-structured.

The namespace, imports, class declaration, and command metadata methods (getName, getDescription, configure) follow standard conventions. The options for --username and --email are correctly defined with short aliases.


45-104: Password policy loading correctly addresses configuration concerns.

The implementation properly loads password requirements from the Registry settings (auth.passwords.*) and applies them to the hasher. The fallback to defaults on exception is reasonable behavior.

Regarding the static analysis hint about unused $parameters: this is a false positive since the parameter is required by the parent Command::execute() signature.


106-152: User lookup logic is sound.

The implementation correctly handles both CLI options and interactive prompts, distinguishes between email and username using filter_var, and provides clear error messages when the user is not found.


202-229: Password update logic correctly handles failure cases.

The implementation now properly checks the return value from $repository->update() (lines 212-218) and reports an appropriate error on failure. The lockout clearing (lines 208-210) is a good security practice for password resets.


237-257: Repository factory method is well-designed for testability.

The getUserRepository() method properly handles configuration errors with clear messaging and is protected to allow mocking in tests.

tests/Unit/Cms/Cli/Commands/User/ResetPasswordCommandTest.php (13)

23-52: Test setup and teardown are correctly implemented.

The virtual filesystem setup, test dependencies initialization, and Registry cleanup in tearDown ensure proper test isolation.


54-67: LGTM - Command metadata test is correct.


69-96: LGTM - Missing config test properly verifies error handling.


98-160: Thorough success path test with good assertions.

The test verifies the complete flow including prompt count, repository interactions, and password hash change via the callback assertion.


162-213: LGTM - Email flow test properly exercises the alternative lookup path.


215-242: LGTM - User not found scenario is correctly tested.


244-283: Good test for cancellation flow.

The expects($this->never())->method('update') assertion ensures the database is not modified when the user declines.


285-321: LGTM - Password validation failure test is correct.


323-360: LGTM - Password mismatch test is correct.


362-410: LGTM - Username option test correctly verifies prompt bypass.

The assertion that only 3 prompts are shown (vs 4 in interactive mode) validates the option behavior.


412-460: LGTM - Email option test correctly verifies prompt bypass.


462-505: Important test for database failure handling.

This test validates that the command properly handles the case when repository->update() returns false, ensuring the fix for the previously flagged issue is working correctly.


507-566: Good coverage of password policy configuration.

The test properly mocks the settings to return custom password requirements and verifies that a password failing those requirements (abcdefghij - no uppercase, no numbers) results in exit code 1.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@ljonesfl ljonesfl closed this Jan 15, 2026
@ljonesfl ljonesfl deleted the feature/cli-password-reset branch January 15, 2026 17:14
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.

2 participants