Skip to content
Open
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
29 changes: 29 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Unit Tests

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@v6

- uses: shivammathur/setup-php@v2
with:
# no matrix across 8.2-8.4 here: these are plain PHP unit tests with no
# version-sensitive logic, so one version is enough signal for the added CI cost
php-version: '8.4'
coverage: none

- name: Install dependencies
run: composer install --no-plugins --prefer-dist

- name: Run unit tests
run: vendor/bin/phpunit Tests/Unit
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@ vendor/
# composer
composer.lock

# phpunit
.phpunit.result.cache
phpunit.xml.dist

# IDEs
.idea/
55 changes: 33 additions & 22 deletions Classes/Controller/ResetPasswordController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Neos\Flow\Property\TypeConverter\PersistentObjectConverter;
use Sandstorm\UserManagement\Domain\Model\ResetPasswordFlow;
use Sandstorm\UserManagement\Domain\Repository\ResetPasswordFlowRepository;
use Sandstorm\UserManagement\Domain\Service\FindEmailAddressForUserServiceInterface;
use Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Mvc\Controller\ActionController;
Expand Down Expand Up @@ -40,6 +41,12 @@ class ResetPasswordController extends ActionController
*/
protected $emailService;

/**
* @Flow\Inject
* @var FindEmailAddressForUserServiceInterface
*/
protected $findEmailAddressForUserService;

/**
* @Flow\Inject
* @var Translator
Expand Down Expand Up @@ -104,28 +111,32 @@ public function requestTokenAction(ResetPasswordFlow $resetPasswordFlow)
}
}

// Send out a confirmation mail
$resetPasswordLink = $this->uriBuilder->reset()->setCreateAbsoluteUri(true)->uriFor(
'insertNewPassword',
['token' => $resetPasswordFlow->getResetPasswordToken()],
'ResetPassword');

$this->emailService->sendTemplateEmail(
'ResetPasswordToken',
$this->getSubjectResetPassword(),
[$resetPasswordFlow->getEmail()],
[
'resetPasswordLink' => $resetPasswordLink,
'resetPasswordFlow' => $resetPasswordFlow
],
'sandstorm_usermanagement_sender_email',
[], // cc
[], // bcc
[], // attachments
'sandstorm_usermanagement_replyTo_email'
);

$this->resetPasswordFlowRepository->add($resetPasswordFlow);
$receiverMail = $this->findEmailAddressForUserService->getEmailAddressByAccount($account);

if ($receiverMail !== null) {
// Send out a confirmation mail
$resetPasswordLink = $this->uriBuilder->reset()->setCreateAbsoluteUri(true)->uriFor(
'insertNewPassword',
['token' => $resetPasswordFlow->getResetPasswordToken()],
'ResetPassword');

$this->emailService->sendTemplateEmail(
'ResetPasswordToken',
$this->getSubjectResetPassword(),
[$receiverMail],
[
'resetPasswordLink' => $resetPasswordLink,
'resetPasswordFlow' => $resetPasswordFlow
],
'sandstorm_usermanagement_sender_email',
[], // cc
[], // bcc
[], // attachments
'sandstorm_usermanagement_replyTo_email'
);

$this->resetPasswordFlowRepository->add($resetPasswordFlow);
}
}


Expand Down
1 change: 0 additions & 1 deletion Classes/Domain/Model/ResetPasswordFlow.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class ResetPasswordFlow
/**
* @var string
* @Flow\Validate(type="NotEmpty")
* @Flow\Validate(type="EmailAddress")
*/
protected $email;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php


namespace Sandstorm\UserManagement\Domain\Service;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Security\Account;

/**
* @api
* @Flow\Scope("singleton")
*/
class FindEmailAddressForUserByAccountIdentifierService implements FindEmailAddressForUserServiceInterface
{
/**
* @param Account $account
* @return string|null
*/
public function getEmailAddressByAccount(Account $account)
{
return $account->getAccountIdentifier();
}
}
20 changes: 20 additions & 0 deletions Classes/Domain/Service/FindEmailAddressForUserServiceInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php


namespace Sandstorm\UserManagement\Domain\Service;


use Neos\Flow\Annotations as Flow;
use Neos\Flow\Security\Account;

/**
* @api
*/
interface FindEmailAddressForUserServiceInterface
{
/**
* @param Account $account
* @return string|null
*/
public function getEmailAddressByAccount(Account $account);
}
2 changes: 2 additions & 0 deletions Configuration/Objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ Sandstorm\UserManagement\Domain\Service\RedirectTargetServiceInterface:
className: 'Sandstorm\UserManagement\Domain\Service\Flow\FlowRedirectTargetService'
Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface:
className: 'Sandstorm\UserManagement\Domain\Service\Flow\FlowUserCreationService'
Sandstorm\UserManagement\Domain\Service\FindEmailAddressForUserServiceInterface:
className: 'Sandstorm\UserManagement\Domain\Service\FindEmailAddressForUserByAccountIdentifierService'
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ Sandstorm\UserManagement\Domain\Service\UserCreationServiceInterface:
className: 'Your\Package\Domain\Service\YourCustomUserCreationService'
```

## Customizing how the reset-password e-mail address is resolved
By default, the "forgot password" flow sends the reset link to the account identifier the user entered (i.e. username
and e-mail address are assumed to be identical). If your application decouples usernames from e-mail addresses, you
can override how the recipient address is resolved by implementing `FindEmailAddressForUserServiceInterface` and
wiring it up via `Objects.yaml`:
```YAML
Sandstorm\UserManagement\Domain\Service\FindEmailAddressForUserServiceInterface:
className: 'Your\Package\Domain\Service\YourCustomFindEmailAddressForUserService'
```

## Hooking into the login/logout process
The UserManagement package emits three signals during the login and logout process, into which you can hook
using Flows [Signals and Slots](http://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/SignalsAndSlots.html)
Expand Down Expand Up @@ -370,8 +380,11 @@ class RegistrationFlowValidationService implements RegistrationFlowValidationSer
```

# 4. Running Tests
Run all unit tests with:
`./bin/phpunit -c ./Build/BuildEssentials/PhpUnit/UnitTests.xml Packages/Application/Sandstorm.UserManagement/Tests/Unit`
Unit tests run standalone in this package, no Flow distribution required:
```
composer install
vendor/bin/phpunit Tests/Unit
```

To run E2E tests, see [E2E Test Readme](Tests/E2E/README.md)

Expand Down
46 changes: 11 additions & 35 deletions Tests/Unit/Domain/PasswordDtoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@
use Sandstorm\UserManagement\Domain\Model\PasswordDto;

/**
* Testcase for the package class
* Testcase for PasswordDto
*
*/
class PackageTest extends UnitTestCase
class PasswordDtoTest extends UnitTestCase
{
public function setUp()
public function setUp(): void
{
}

/**
* @test
*/
public function equalPasswordsAreEqual()
public function testEqualPasswordsAreEqual()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('foobar');
Expand All @@ -26,10 +23,7 @@ public function equalPasswordsAreEqual()
$this->assertTrue($passwordDto->arePasswordsEqual());
}

/**
* @test
*/
public function inequalPasswordsAreNotEqual()
public function testInequalPasswordsAreNotEqual()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('FOOBAR');
Expand All @@ -38,10 +32,7 @@ public function inequalPasswordsAreNotEqual()
$this->assertFalse($passwordDto->arePasswordsEqual());
}

/**
* @test
*/
public function passwordMinLength()
public function testPasswordMinLength()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('6chars');
Expand All @@ -51,10 +42,7 @@ public function passwordMinLength()
$this->assertFalse($passwordDto->isPasswordMinLength(7));
}

/**
* @test
*/
public function passwordMaxLength()
public function testPasswordMaxLength()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('6chars');
Expand All @@ -64,10 +52,7 @@ public function passwordMaxLength()
$this->assertFalse($passwordDto->isPasswordMaxLength(5));
}

/**
* @test
*/
public function passwordContainsLowercaseLetters()
public function testPasswordContainsLowercaseLetters()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('4loweRCASELETTERS');
Expand All @@ -78,10 +63,7 @@ public function passwordContainsLowercaseLetters()
$this->assertFalse($passwordDto->doesPasswordContainLowercaseLetters(5));
}

/**
* @test
*/
public function passwordContainsUppercaseLetters()
public function testPasswordContainsUppercaseLetters()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('4UPPErcaseletters');
Expand All @@ -92,10 +74,7 @@ public function passwordContainsUppercaseLetters()
$this->assertFalse($passwordDto->doesPasswordContainUppercaseLetters(5));
}

/**
* @test
*/
public function passwordContainsNumbers()
public function testPasswordContainsNumbers()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('fournumbers1234');
Expand All @@ -106,10 +85,7 @@ public function passwordContainsNumbers()
$this->assertFalse($passwordDto->doesPasswordContainNumbers(5));
}

/**
* @test
*/
public function passwordContainsSpecialCharacters()
public function testPasswordContainsSpecialCharacters()
{
$passwordDto = new PasswordDto();
$passwordDto->setPassword('4specialCHARS!"%$');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php
namespace Sandstorm\UserManagement\Tests\Unit\Domain\Service;

use Neos\Flow\Security\Account;
use Neos\Flow\Tests\UnitTestCase;
use Sandstorm\UserManagement\Domain\Service\FindEmailAddressForUserByAccountIdentifierService;

/**
* Testcase for the FindEmailAddressForUserByAccountIdentifierService
*
*/
class FindEmailAddressForUserByAccountIdentifierServiceTest extends UnitTestCase
{
public function testReturnsTheAccountIdentifierWhenItIsAnEmailAddress()
{
$account = new Account();
$account->setAccountIdentifier('user@example.com');

$service = new FindEmailAddressForUserByAccountIdentifierService();

$this->assertSame('user@example.com', $service->getEmailAddressByAccount($account));
}

public function testReturnsTheAccountIdentifierUnchangedWhenItIsAPlainUsername()
{
$account = new Account();
$account->setAccountIdentifier('someuser');

$service = new FindEmailAddressForUserByAccountIdentifierService();

$this->assertSame('someuser', $service->getEmailAddressByAccount($account));
}
}
9 changes: 9 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,18 @@
"neos/flow": "^8.3 || ^9.0",
"sandstorm/templatemailer": "^3.0.1 || dev-master"
},
"require-dev": {
"phpunit/phpunit": "^13.0"
},
"autoload": {
"psr-4": {
"Sandstorm\\UserManagement\\": "Classes"
}
},
"autoload-dev": {
"psr-4": {
"Sandstorm\\UserManagement\\Tests\\": "Tests",
"Neos\\Flow\\Tests\\": "vendor/neos/flow/Tests"
}
}
}