Commit 53265d61 authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Finish get use case and refactor doorcode to a hashed searchable entity

Use that hashed searchable entity for doors
parent 6382417b
......@@ -105,7 +105,7 @@ class DoorGuard implements Guard
{
$presenter = new TranslationPresenter();
$this->authenticator->check($presenter, $token);
$this->authenticator->check($presenter, config('app.key'), $token);
return $presenter->getViewModel();
}
......
......@@ -10,6 +10,7 @@ use Source\Gateways\Users\UsersRepositoryServiceProvider;
use Source\Gateways\Groups\GroupsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider;
use Source\UseCases\Doors\GetDoor\GetDoorUseCaseServiceProvider;
use Source\Gateways\GroupUser\GroupUserRepositoryServiceProvider;
use Source\UseCases\Groups\GetGroup\GetGroupUseCaseServiceProvider;
use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider;
......@@ -68,6 +69,7 @@ class AppServiceProvider extends ServiceProvider
RemoveUserFromGroupUseCaseServiceProvider::class,
// Doors
GetDoorUseCaseServiceProvider::class,
AuthenticateUseCaseServiceProvider::class,
DoorAuthenticateUseCaseServiceProvider::class,
UserAuthenticateUseCaseServiceProvider::class,
......
......@@ -14,13 +14,12 @@ class CreateDoorsTable extends Migration
public function up()
{
// This table contains the base door objects stored on the system
// TODO: Map db entry to physical door
Schema::create('doors', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('location');
$table->string('name')->unique();
// hashed
$table->string('api_token');
$table->string('api_token')->unique();
$table->timestamps();
});
}
......
......@@ -13,25 +13,25 @@ class Door
protected string $name;
protected string $token;
protected HashedSearchable $token;
protected ?Carbon $createdAt;
protected ?Carbon $updatedAt;
/**
* @param int $id
* @param string $location
* @param string $name
* @param string $token
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
* @param int $id
* @param string $location
* @param string $name
* @param \Source\Entities\HashedSearchable $token
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
*/
public function __construct(
int $id,
string $location,
string $name,
string $token,
HashedSearchable $token,
?Carbon $createdAt = null,
?Carbon $updatedAt = null
) {
......@@ -83,20 +83,33 @@ class Door
return $this->updatedAt;
}
public function hasTokenOf(?string $token): bool
public function hasTokenOf(?HashedSearchable $token): bool
{
if (!$token) {
return false;
}
return $this->getToken() === $token;
return $this->getToken()->getHash() === $token->getHash();
}
/**
* @return string
* @return \Source\Entities\HashedSearchable
*/
public function getToken(): string
public function getToken(): HashedSearchable
{
return $this->token;
}
/**
* @param string $id
* @return bool
*/
public function hasIdOf(?string $id): bool
{
if (!$id) {
return false;
}
return $this->id === (int)$id;
}
}
......@@ -3,7 +3,7 @@
namespace Source\Entities;
class Doorcode
class HashedSearchable
{
/**
* @var string
......@@ -11,7 +11,7 @@ class Doorcode
protected string $hash;
/**
* Construct from existing Doorcode hash
* Construct from existing hash
*
* @param string $hash
*/
......@@ -33,8 +33,8 @@ class Doorcode
// Two rounds of sha512 each salted
// As of PHP 7 the salt parameter to password_hash has been depreciated.
// As we need to be able to search for users by doorcode they either all need to have the same salt
// or no salt (I'd prefer a salt). This way the doorcode can be hashed and
// As we need to be able to search for these values they either all need to have the same salt
// or no salt (I'd prefer a salt). This way the value can be hashed and
// then easily searched for in the database without having to check against every user and rehash every
// time with the new salt. As such it is not as easy to use BCRYPT :(
// So I'll just shred it twice using sha512 with with (probably) the application key.
......
......@@ -43,9 +43,9 @@ class User
protected ?Password $password;
/**
* @var \Source\Entities\Doorcode|null
* @var \Source\Entities\HashedSearchable|null
*/
protected ?Doorcode $doorcode;
protected ?HashedSearchable $doorcode;
/**
* @var Carbon|null
......@@ -63,17 +63,17 @@ class User
protected ?Carbon $updatedAt;
/**
* @param int $id
* @param string $firstName
* @param string $lastName
* @param string $displayName
* @param string $email
* @param string|null $emplid
* @param \Source\Entities\Password|null $password
* @param \Source\Entities\Doorcode|null $doorcode
* @param Carbon|null $expiresAt
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
* @param int $id
* @param string $firstName
* @param string $lastName
* @param string $displayName
* @param string $email
* @param string|null $emplid
* @param \Source\Entities\Password|null $password
* @param \Source\Entities\HashedSearchable|null $doorcode
* @param Carbon|null $expiresAt
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
*/
public function __construct(
int $id,
......@@ -83,7 +83,7 @@ class User
string $email,
?string $emplid = null,
?Password $password = null,
?Doorcode $doorcode = null,
?HashedSearchable $doorcode = null,
?Carbon $expiresAt = null,
?Carbon $createdAt = null,
?Carbon $updatedAt = null
......@@ -220,19 +220,19 @@ class User
* @param string $doorcode
* @return bool
*/
public function hasDoorcodeOf(?Doorcode $doorcode): bool
public function hasDoorcodeOf(?HashedSearchable $doorcode): bool
{
if (!$doorcode || !$this->getDoorcode()) {
return false;
}
return $this->getDoorcode()->getHash() === $doorcode;
return $this->getDoorcode()->getHash() === $doorcode->getHash();
}
/**
* @return \Source\Entities\Doorcode|null
* @return \Source\Entities\HashedSearchable|null
*/
public function getDoorcode(): ?Doorcode
public function getDoorcode(): ?HashedSearchable
{
return $this->doorcode;
}
......
......@@ -4,6 +4,7 @@
namespace Source\Gateways\Doors;
use Source\Entities\Door;
use Source\Entities\HashedSearchable;
class DatabaseDoorsRepository implements DoorsRepository
{
......@@ -16,7 +17,7 @@ class DatabaseDoorsRepository implements DoorsRepository
$dbDoor->location = $door->getLocation();
$dbDoor->name = $door->getName();
$dbDoor->api_token = hash('sha512', $door->getToken());
$dbDoor->api_token = $door->getToken()->getHash();
$dbDoor->save();
......@@ -24,7 +25,7 @@ class DatabaseDoorsRepository implements DoorsRepository
$dbDoor->id,
$dbDoor->location,
$dbDoor->name,
$dbDoor->api_token,
new HashedSearchable($dbDoor->api_token),
$dbDoor->createdAt,
$dbDoor->updatedAt
);
......@@ -33,9 +34,9 @@ class DatabaseDoorsRepository implements DoorsRepository
/**
* @inheritDoc
*/
public function getByToken(string $token): ?Door
public function getByToken(?HashedSearchable $token): ?Door
{
$dbDoor = \App\Door::where('api_token', hash('sha512', $token))->first();
$dbDoor = \App\Door::where('api_token', $token->getHash())->first();
if (!$dbDoor) {
return null;
......@@ -45,9 +46,17 @@ class DatabaseDoorsRepository implements DoorsRepository
$dbDoor->id,
$dbDoor->location,
$dbDoor->name,
$dbDoor->api_token,
new HashedSearchable($dbDoor->api_token),
$dbDoor->createdAt,
$dbDoor->updatedAt
);
}
/**
* @inheritDoc
*/
public function get(string $doorId): ?Door
{
return \App\Door::find((int)$doorId);
}
}
......@@ -4,6 +4,7 @@
namespace Source\Gateways\Doors;
use Source\Entities\Door;
use Source\Entities\HashedSearchable;
interface DoorsRepository
{
......@@ -18,8 +19,16 @@ interface DoorsRepository
/**
* Attempt to find a door by a token
*
* @param string $token
* @param \Source\Entities\HashedSearchable|null $token
* @return Door|null
*/
public function getByToken(string $token): ?Door;
public function getByToken(?HashedSearchable $token): ?Door;
/**
* Get a door by ID
*
* @param string $doorId
* @return \Source\Entities\Door|null
*/
public function get(string $doorId): ?Door;
}
......@@ -4,6 +4,7 @@
namespace Source\Gateways\Doors;
use Source\Entities\Door;
use Source\Entities\HashedSearchable;
class InMemoryDoorsRepository implements DoorsRepository
{
......@@ -23,7 +24,7 @@ class InMemoryDoorsRepository implements DoorsRepository
/**
* @inheritDoc
*/
public function getByToken(string $token): ?Door
public function getByToken(?HashedSearchable $token): ?Door
{
foreach ($this->doors as $door) {
if ($door->hasTokenOf($token)) {
......@@ -33,4 +34,18 @@ class InMemoryDoorsRepository implements DoorsRepository
return null;
}
/**
* @inheritDoc
*/
public function get(string $doorId): ?Door
{
foreach ($this->doors as $door) {
if ($door->hasIdOf($doorId)) {
return $door;
}
}
return null;
}
}
......@@ -4,12 +4,14 @@
namespace Source\Gateways\Doors;
use Source\Entities\Door;
use Source\Entities\HashedSearchable;
class LocalDoorsRepository extends InMemoryDoorsRepository
{
public function __construct()
{
$this->doors[] = new Door(1, 'The Amazon', 'chicken izta door', 'door_1_api_token');
$this->doors[] = new Door(2, 'Bat Cave', 'Bruce\' lair', 'door_2_api_token');
$salt = config('app.key');
$this->doors[] = new Door(1, 'The Amazon', 'chicken izta door', HashedSearchable::hash($salt, 'door_1_api_token'));
$this->doors[] = new Door(2, 'Bat Cave', 'Bruce\' lair', HashedSearchable::hash($salt, 'door_2_api_token'));
}
}
......@@ -4,7 +4,7 @@
namespace Source\Gateways\Users;
use Source\Entities\User;
use Source\Entities\Doorcode;
use Source\Entities\HashedSearchable;
use Source\Entities\Password;
class DatabaseUsersRepository implements UsersRepository
......@@ -37,7 +37,7 @@ class DatabaseUsersRepository implements UsersRepository
$user->email,
$user->emplid,
$user->password === null ? null : new Password($user->password),
$user->doorcode === null ? null : new Doorcode($user->doorcode),
$user->doorcode === null ? null : new HashedSearchable($user->doorcode),
$user->expires_at,
$user->created_at,
$user->updated_at
......@@ -127,7 +127,7 @@ class DatabaseUsersRepository implements UsersRepository
/**
* @inheritDoc
*/
public function findByDoorcode(?Doorcode $doorcode): ?User
public function findByDoorcode(?HashedSearchable $doorcode): ?User
{
if (!$doorcode) {
return null;
......
......@@ -4,7 +4,7 @@
namespace Source\Gateways\Users;
use Source\Entities\User;
use Source\Entities\Doorcode;
use Source\Entities\HashedSearchable;
class InMemoryUsersRepository implements UsersRepository
{
......@@ -98,7 +98,7 @@ class InMemoryUsersRepository implements UsersRepository
/**
* @inheritDoc
*/
public function findByDoorcode(?Doorcode $doorcode): ?User
public function findByDoorcode(?HashedSearchable $doorcode): ?User
{
foreach ($this->users as $user) {
if ($user->hasDoorcodeOf($doorcode)) {
......
......@@ -5,6 +5,8 @@ namespace Source\Gateways\Users;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\Password;
use Source\Entities\HashedSearchable;
class LocalUsersRepository extends InMemoryUsersRepository
{
......@@ -34,8 +36,8 @@ class LocalUsersRepository extends InMemoryUsersRepository
'The Emperor',
'sithL0rd@senate.com',
'execute order 66',
'I am the senate',
'123456',
Password::hash('I am the senate'),
HashedSearchable::hash(config('app.key'), '123456'),
Carbon::now()->addDays(3),
Carbon::now()->subDays(3),
Carbon::now()
......@@ -55,8 +57,8 @@ class LocalUsersRepository extends InMemoryUsersRepository
'Cobain Bryant',
'he ded',
'299012',
'kobe didn\' miss his last shot',
'12453',
Password::hash('kobe didn\' miss his last shot'),
HashedSearchable::hash(config('app.key'), '12453'),
new Carbon('2020-01-26 09:06:00'),
null,
null
......@@ -76,8 +78,8 @@ class LocalUsersRepository extends InMemoryUsersRepository
'JD Priddy',
'email idk',
'201565',
'not gonna be plaintext just placeholder here',
'123866',
Password::hash('not gonna be plaintext just placeholder here'),
HashedSearchable::hash(config('app.key'), '123866'),
null,
new Carbon('2020-02-02 02:02:02'),
Carbon::now()
......@@ -97,8 +99,8 @@ class LocalUsersRepository extends InMemoryUsersRepository
'JJ Obob',
'jarod.owen@wallawalla.edu',
'177013',
'not rlly plaintext password',
'42069',
Password::hash('not rlly plaintext password'),
HashedSearchable::hash(config('app.key'), '42069'),
null,
new Carbon('2020-02-02 13:30:35'),
null
......
......@@ -4,7 +4,7 @@
namespace Source\Gateways\Users;
use Source\Entities\User;
use Source\Entities\Doorcode;
use Source\Entities\HashedSearchable;
interface UsersRepository
{
......@@ -47,10 +47,10 @@ interface UsersRepository
/**
* Find a user by doorcode
*
* @param \Source\Entities\Doorcode|null $doorcode
* @param \Source\Entities\HashedSearchable|null $doorcode
* @return User|null
*/
public function findByDoorcode(?Doorcode $doorcode): ?User;
public function findByDoorcode(?HashedSearchable $doorcode): ?User;
/**
* Find a user by email
......
......@@ -2,6 +2,7 @@
namespace Source\UseCases\Doors\Authenticate;
use Source\Entities\HashedSearchable;
use Source\Gateways\Doors\DoorsRepository;
class Authenticate implements AuthenticateUseCase
......@@ -22,12 +23,12 @@ class Authenticate implements AuthenticateUseCase
/**
* @inheritDoc
*/
public function check(Presenter $presenter, ?string $token): void
public function check(Presenter $presenter, string $salt, ?string $token): void
{
$found = null;
if ($token) {
$found = $this->doors->getByToken($token);
$found = $this->doors->getByToken(HashedSearchable::hash($salt, $token));
}
$response = new ResponseModel();
......
......@@ -9,7 +9,8 @@ interface AuthenticateUseCase
* Authenticates a door token
*
* @param Presenter $presenter
* @param string $salt
* @param string|null $token
*/
public function check(Presenter $presenter, ?string $token): void;
public function check(Presenter $presenter, string $salt, ?string $token): void;
}
<?php
namespace Source\UseCases\Doors\GetDoor;
use Source\UseCases\BasePresenter;
class APIPresenter extends BasePresenter implements Presenter
{
protected array $viewModel = [];
/** @inheritDoc */
public function present(ResponseModel $responseModel): void
{
$door = $responseModel->getDoor();
$this->viewModel['door'] = [
'id' => $door->getId(),
'name' => $door->getName(),
'location' => $door->getLocation(),
'created_at' => $door->getCreatedAt(),
'updated_at' => $door->getUpdatedAt(),
];
}
/** @inheritDoc */
public function getViewModel(): array
{
return $this->viewModel;
}
}
<?php
namespace Source\UseCases\Doors\GetDoor;
use Source\Gateways\Doors\DoorsRepository;
use Source\Exceptions\EntityNotFoundException;
class GetDoor implements GetDoorUseCase
{
/**
* @var \Source\Gateways\Doors\DoorsRepository
*/
protected DoorsRepository $doorsRepository;
/**
* @param \Source\Gateways\Doors\DoorsRepository $doorsRepository
*/
public function __construct(DoorsRepository $doorsRepository)
{
$this->doorsRepository = $doorsRepository;
}
/**
* @inheritDoc
*/
public function get(string $doorId, Presenter $presenter): void
{
$door = $this->doorsRepository->get($doorId);
if (!$door) {
throw new EntityNotFoundException();
}
$response = new ResponseModel($door);
$presenter->present($response);
}
}
<?php
namespace Source\UseCases\Doors\GetDoor;
interface GetDoorUseCase
{
/**
* Get a door
*
* @param string $doorId
* @param \Source\UseCases\Doors\GetDoor\Presenter $presenter
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function get(string $doorId, Presenter $presenter): void;
}
<?php
namespace Source\UseCases\Doors\GetDoor;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Doors\DoorsRepository;
use Illuminate\Contracts\Foundation\Application;