Commit 8e3bf4df authored by Jacob Priddy's avatar Jacob Priddy 👌

Reject expired users from using the application.

parent 9071e159
Pipeline #11827 failed with stages
in 3 minutes and 7 seconds
......@@ -93,6 +93,7 @@ class AuthController extends ApiController
* @return mixed
* @throws EntityNotFoundException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function handle(AuthenticateUseCase $authenticateUseCase)
{
......
......@@ -283,6 +283,7 @@ class User
public function isActiveForDate(Carbon $date): bool
{
if (!$this->getExpiresAt()) {
// User does not expire.
return true;
}
......
......@@ -3,14 +3,12 @@
namespace Source\UseCases\Door\Access\Authorizers;
use Carbon\Carbon;
use Source\Entities\User;
use Source\UseCases\Door\Access\AccessAuthorizer;
class ExpiredAuthorizer implements AccessAuthorizer
{
/**
* @inheritDoc
*/
......
......@@ -2,6 +2,7 @@
namespace Source\UseCases\Token\Authenticate;
use Carbon\Carbon;
use Source\Entities\HashedSearchable;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Tokens\TokensRepository;
......@@ -48,6 +49,10 @@ class Authenticate implements AuthenticateUseCase
$user = $this->users->get($found->getUserId());
if ($user && !$user->isActiveForDate(Carbon::now())) {
return;
}
$response->setUser($user);
$presenter->present($response);
......
......@@ -20,15 +20,15 @@ class TranslationPresenter extends BasePresenter implements Presenter
$this->viewModel = new User();
$this->viewModel->id = $user->getId();
$this->viewModel->email = $user->getEmail();
$this->viewModel->first_name = $user->getFirstName();
$this->viewModel->last_name = $user->getLastName();
$this->viewModel->display_name = $user->getDisplayName();
$this->viewModel->emplid = $user->getEmplid();
$this->viewModel->created_at = $user->getCreatedAt();
$this->viewModel->expires_at = $user->getExpiresAt();
$this->viewModel->updated_at = $user->getUpdatedAt();
$this->viewModel->setAttribute('id', $user->getId());
$this->viewModel->setAttribute('email', $user->getEmail());
$this->viewModel->setAttribute('first_name', $user->getFirstName());
$this->viewModel->setAttribute('last_name', $user->getLastName());
$this->viewModel->setAttribute('display_name', $user->getDisplayName());
$this->viewModel->setAttribute('emplid', $user->getEmplid());
$this->viewModel->setAttribute('expires_at', $user->getExpiresAt());
$this->viewModel->setAttribute('created_at', $user->getCreatedAt());
$this->viewModel->setAttribute('updated_at', $user->getUpdatedAt());
}
/** @inheritDoc */
......
......@@ -2,11 +2,13 @@
namespace Source\UseCases\Users\Authenticate;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\HashedSearchable;
use Source\Gateways\Saml\SamlRepository;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Tokens\TokensRepository;
use Source\Exceptions\AuthorizationException;
use Source\Exceptions\AuthenticationException;
class Authenticate implements AuthenticateUseCase
......@@ -58,7 +60,11 @@ class Authenticate implements AuthenticateUseCase
if (!$user ||
!$user->getPassword() ||
!$user->isActiveForDate(Carbon::now()) ||
!$user->getPassword()->matches($password)) {
/*
* We must have found a user, they must be active, and their password must be correct
*/
throw new AuthenticationException();
}
......@@ -121,6 +127,10 @@ class Authenticate implements AuthenticateUseCase
throw new UserCreationException();
}
if (!$user->isActiveForDate(Carbon::now())) {
throw new AuthorizationException();
}
$token = $this->tokens->createLoginToken($user->getId(), $this->salt);
$response = new ResponseModel($user, $token->getRaw(), $token->getToken());
......
......@@ -41,6 +41,7 @@ interface AuthenticateUseCase
* @throws UserCreationException
* @throws EntityNotFoundException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function handleSamlLogin(Presenter $presenter): void;
}
......@@ -3,7 +3,6 @@
namespace Tests\Unit\Source\UseCases\Door\Access\Authorizers;
use Carbon\Carbon;
use Tests\TestCase;
use Source\Entities\User;
......
<?php
namespace Tests\Unit\Source\UseCases\Token\Authenticate;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\Token;
use PHPUnit\Framework\TestCase;
use Source\Entities\HashedSearchable;
use Tests\Unit\Guards\TokenPresenterStub;
use Source\Gateways\Users\InMemoryUsersRepository;
use Source\Gateways\Tokens\InMemoryTokensRepository;
use Source\UseCases\Token\Authenticate\Authenticate;
use Source\UseCases\Token\Authenticate\ResponseModel;
class TokenAuthenticateTest extends TestCase
{
protected const SALT = '155884';
/**
* @var \Source\UseCases\Token\Authenticate\Authenticate
*/
protected Authenticate $useCase;
/**
* @var \Source\Gateways\Users\InMemoryUsersRepository
*/
protected InMemoryUsersRepository $users;
/**
* @var \Source\Gateways\Tokens\InMemoryTokensRepository
*/
protected InMemoryTokensRepository $tokens;
/**
* @var \Tests\Unit\Guards\TokenPresenterStub
*/
protected TokenPresenterStub $presenter;
protected ResponseModel $response;
protected function setUp(): void
{
parent::setUp();
$this->tokens = new InMemoryTokensRepository();
$this->users = new InMemoryUsersRepository();
$this->useCase = new Authenticate($this->tokens, $this->users, self::SALT);
$this->presenter = new TokenPresenterStub();
}
/**
* @param string|null $token
*/
protected function handleTest(?string $token): void
{
$this->useCase->check($this->presenter, $token);
$this->response = $this->presenter->response;
}
/**
* @param \Carbon\Carbon|null $expires
* @return \Source\Entities\User
* @throws \Source\Exceptions\EntityExistsException
*/
protected function createUser(?Carbon $expires = null): User
{
return $this->users->create(new User(
0,
'',
'',
'',
'',
null,
null,
null,
$expires
));
}
/**
* @test
*/
public function it_rejects_no_token(): void
{
$this->handleTest(null);
$this->assertNull($this->response->getUser());
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_rejects_no_user_match(): void
{
$user = $this->createUser();
$this->tokens->create(new Token(0, $user->getId() + 10, HashedSearchable::hash(self::SALT, 'token')));
$this->handleTest('token');
$this->assertNull($this->response->getUser());
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_matches_a_token_to_user(): void
{
$user = $this->createUser();
$this->tokens->create(new Token(0, $user->getId(), HashedSearchable::hash(self::SALT, 'token')));
$this->handleTest('token');
$this->assertEquals($user, $this->response->getUser());
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_rejects_expired_users(): void
{
$user = $this->createUser(Carbon::now()->subDay());
$this->tokens->create(new Token(0, $user->getId(), HashedSearchable::hash(self::SALT, 'token')));
$this->handleTest('token');
$this->assertNull($this->response->getUser());
}
}
......@@ -30,10 +30,11 @@ class AttemptUseCaseTest extends UseCaseBaseTest
}
/**
* @param \Carbon\Carbon|null $expires
* @return \Source\Entities\User
* @throws \Source\Exceptions\EntityExistsException
*/
protected function createUser(): User
protected function createUser(?Carbon $expires = null): User
{
return $this->users->create(new User(
0,
......@@ -42,7 +43,9 @@ class AttemptUseCaseTest extends UseCaseBaseTest
'',
self::VALID_EMAIL,
'',
Password::hash(self::VALID_PASS)
Password::hash(self::VALID_PASS),
null,
$expires
));
}
......@@ -127,4 +130,18 @@ class AttemptUseCaseTest extends UseCaseBaseTest
$this->assertEquals($user, $this->response->getUser());
}
/**
* @test
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function it_rejects_expired_users(): void
{
$this->expectException(AuthenticationException::class);
$this->createUser(Carbon::now()->subYear());
$this->handleTest();
}
}
......@@ -8,6 +8,7 @@ use Source\Entities\User;
use Source\Entities\Token;
use Source\Entities\SamlUser;
use Source\Entities\HashedSearchable;
use Source\Exceptions\AuthorizationException;
use Tests\Doubles\InMemoryUsersRepositoryStub;
use Source\UseCases\Users\Authenticate\Authenticate;
use Source\UseCases\Users\Authenticate\UserCreationException;
......@@ -21,6 +22,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
protected function handleLoginTest(?SamlUser $samlUser = null): void
{
......@@ -55,6 +57,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_calls_present_on_presenter(): void
{
......@@ -103,6 +106,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_cannot_create_null_users(): void
{
......@@ -116,6 +120,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_creates_a_user_if_they_do_not_exist_in_local_database(): void
{
......@@ -141,6 +146,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_throws_an_exception_if_it_cannot_create_a_user(): void
{
......@@ -156,6 +162,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_creates_a_token_for_the_user(): void
{
......@@ -178,6 +185,7 @@ class SamlUseCaseTest extends UseCaseBaseTest
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
* @throws \Source\Exceptions\AuthorizationException
*/
public function it_updates_existing_users(): void
{
......@@ -193,4 +201,34 @@ class SamlUseCaseTest extends UseCaseBaseTest
$this->assertEquals($samlUser->getDisplayName(), $users[0]->getDisplayName());
$this->assertEquals($samlUser->getEmail(), $users[0]->getEmail());
}
/**
* @test
*
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityExistsException
* @throws \Source\Exceptions\EntityNotFoundException
* @throws \Source\UseCases\Users\Authenticate\UserCreationException
*/
public function it_rejects_expired_users(): void
{
$samlUser = $this->createSamlUser();
$this->users->create(
new User(
0,
'',
'',
'',
$samlUser->getEmail(),
null,
null,
null,
Carbon::now()->subYear()
)
);
$this->expectException(AuthorizationException::class);
$this->handleLoginTest($samlUser);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment