Commit 2e35b145 authored by Jacob Priddy's avatar Jacob Priddy 👌

Got door authentication implemented

parent da566b89
......@@ -5,12 +5,7 @@ namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
class Door extends Authenticatable {
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'api_token',
protected $fillable = [
'*'
];
}
......@@ -4,8 +4,6 @@
namespace App\Guards;
use App\User;
use App\Token;
use Illuminate\Http\Request;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
......@@ -31,12 +29,8 @@ class ApiGuard implements Guard {
protected string $inputKey;
/**
* The name of the token "column" in persistent storage.
*
* @var string
* @var AuthenticateUseCase
*/
protected string $storageKey;
protected AuthenticateUseCase $authenticator;
/**
......@@ -45,16 +39,13 @@ class ApiGuard implements Guard {
* @param AuthenticateUseCase $authenticator
* @param Request $request
* @param string $inputKey
* @param string $storageKey
*/
public function __construct(
AuthenticateUseCase $authenticator,
Request $request,
$inputKey = 'api_token',
$storageKey = 'api_token') {
$inputKey = 'api_token'){
$this->request = $request;
$this->inputKey = $inputKey;
$this->storageKey = $storageKey;
$this->authenticator = $authenticator;
}
......
<?php
namespace App\Guards;
use Illuminate\Http\Request;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\Authenticatable;
use Source\UseCases\Doors\Authenticate\AuthenticateUseCase;
use Source\UseCases\Doors\Authenticate\TranslationPresenter;
class DoorGuard implements Guard {
use GuardHelpers;
/**
* The request instance.
*
* @var Request
*/
protected Request $request;
/**
* The name of the query string item from the request containing the API token.
*
* @var string
*/
protected string $inputKey;
/**
* @var AuthenticateUseCase
*/
protected AuthenticateUseCase $authenticator;
/**
* Create a new authentication guard.
*
* @param AuthenticateUseCase $authenticator
* @param Request $request
* @param string $inputKey
*/
public function __construct(
AuthenticateUseCase $authenticator,
Request $request,
$inputKey = 'api_token') {
$this->request = $request;
$this->inputKey = $inputKey;
$this->authenticator = $authenticator;
}
/**
* Get the currently authenticated user.
*
* @return Authenticatable|null
*/
public function user() {
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if ($this->user !== null) {
return $this->user;
}
$user = null;
$token = $this->getTokenForRequest();
if (!empty($token)) {
$user = $this->retrieveByToken($token);
}
return $this->user = $user;
}
/**
* Get the token for the current request.
*
* @return string|null
*/
public function getTokenForRequest(): ?string {
$token = $this->request->query($this->inputKey);
if (empty($token)) {
$token = $this->request->input($this->inputKey);
}
if (empty($token)) {
$token = $this->request->bearerToken();
}
if (empty($token)) {
$token = $this->request->getPassword();
}
return $token;
}
/**
* Validate a user's credentials.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = []) {
if (empty($credentials[$this->inputKey])) {
return false;
}
if ($this->retrieveByToken($credentials[$this->inputKey])) {
return true;
}
return false;
}
/**
* Set the current request instance.
*
* @param Request $request
* @return $this
*/
public function setRequest(Request $request): self {
$this->request = $request;
return $this;
}
/**
* @param string $token
*
* @return Authenticatable|null
*/
public function retrieveByToken(string $token): ?Authenticatable {
$presenter = new TranslationPresenter();
$this->authenticator->check($presenter, $token);
return $presenter->getViewModel();
}
}
......@@ -8,8 +8,6 @@ use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\EncryptCookies;
use App\Http\Middleware\VerifyCsrfToken;
use Illuminate\Auth\Middleware\Authorize;
use App\Http\Middleware\AuthenticateDoors;
use App\Http\Middleware\AuthenticateAPIUser;
use Illuminate\Http\Middleware\SetCacheHeaders;
use Illuminate\Auth\Middleware\RequirePassword;
use Illuminate\Session\Middleware\StartSession;
......
......@@ -4,6 +4,7 @@ namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepositoryServiceProvider;
use Source\Gateways\Doors\DoorsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider;
use Source\UseCases\Users\CreateUser\CreateUserUseCaseServiceProvider;
......@@ -11,6 +12,7 @@ use Source\UseCases\Users\DeleteUser\DeleteUserUseCaseServiceProvider;
use Source\UseCases\Users\UpdateUser\UpdateUserUseCaseServiceProvider;
use Source\UseCases\Users\GetAllUsers\GetAllUsersUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
use Source\UseCases\Doors\Authenticate\AuthenticateUseCaseServiceProvider as DoorAuthenticateUseCaseServiceProvider;
class AppServiceProvider extends ServiceProvider
{
......@@ -20,6 +22,7 @@ class AppServiceProvider extends ServiceProvider
*/
protected array $gatewayProviders = [
UsersRepositoryServiceProvider::class,
DoorsRepositoryServiceProvider::class,
TokensRepositoryServiceProvider::class,
];
......@@ -34,6 +37,7 @@ class AppServiceProvider extends ServiceProvider
GetAllUsersUseCaseServiceProvider::class,
AuthenticateUseCaseServiceProvider::class,
DoorAuthenticateUseCaseServiceProvider::class,
];
/**
......
......@@ -3,6 +3,7 @@
namespace App\Providers;
use App\Guards\ApiGuard;
use App\Guards\DoorGuard;
use Illuminate\Support\Facades\Auth;
use Source\UseCases\Token\Authenticate\AuthenticateUseCase;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
......@@ -32,5 +33,13 @@ class AuthServiceProvider extends ServiceProvider {
return new ApiGuard($app->make(AuthenticateUseCase::class), $app['request']);
}
);
// Define guard for the door api
Auth::extend(
'door',
static function ($app, $name, array $config) {
return new DoorGuard($app->make(\Source\UseCases\Doors\Authenticate\AuthenticateUseCase::class), $app['request']);
}
);
}
}
......@@ -7,6 +7,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Token extends Model
{
protected $fillable = [
'*'
];
protected $casts = [
'expires_at' => 'datetime',
];
......
......@@ -43,14 +43,11 @@ return [
'api' => [
'driver' => 'api',
'provider' => 'users',
],
'door' => [
'driver' => 'token',
'provider' => 'doors',
'hash' => true,
]
'driver' => 'door',
],
],
/*
......
......@@ -20,7 +20,7 @@ class CreateDoorsTable extends Migration
$table->string('location');
$table->string('name')->unique();
// hashed
$table->string('api_token')->unique();
$table->string('api_token');
$table->timestamps();
});
}
......
<?php
namespace Source\Entities;
use Carbon\Carbon;
class Door {
protected int $id;
protected string $location;
protected string $name;
protected string $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
*/
public function __construct(int $id,
string $location,
string $name,
string $token,
?Carbon $createdAt = null,
?Carbon $updatedAt = null) {
$this->id = $id;
$this->location = $location;
$this->name = $name;
$this->token = $token;
$this->createdAt = $createdAt;
$this->updatedAt = $updatedAt;
}
/**
* @return int
*/
public function getId(): int {
return $this->id;
}
/**
* @return string
*/
public function getLocation(): string {
return $this->location;
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* @return string
*/
public function getToken(): string {
return $this->token;
}
/**
* @return Carbon|null
*/
public function getCreatedAt(): ?Carbon {
return $this->createdAt;
}
/**
* @return Carbon|null
*/
public function getUpdatedAt(): ?Carbon {
return $this->updatedAt;
}
public function hasTokenOf(?string $token): bool {
if (!$token) {
return false;
}
return $this->getToken() === $token;
}
}
<?php
namespace Source\Gateways\Doors;
use Source\Entities\Door;
class DatabaseDoorsRepository implements DoorsRepository {
/**
* @inheritDoc
*/
public function create(Door $door): Door {
$dbDoor = \App\Door::create(
[
'location' => $door->getLocation(),
'name' => $door->getName(),
'token' => hash('sha256', $door->getToken()),
]
);
return new Door(
$dbDoor->id,
$dbDoor->location,
$dbDoor->name,
$dbDoor->api_token,
$dbDoor->createdAt,
$dbDoor->updatedAt
);
}
/**
* @inheritDoc
*/
public function getByToken(string $token): ?Door {
$dbDoor = \App\Door::where('api_token', hash('sha256', $token))->first();
if (!$dbDoor) {
return null;
}
return new Door(
$dbDoor->id,
$dbDoor->location,
$dbDoor->name,
$dbDoor->api_token,
$dbDoor->createdAt,
$dbDoor->updatedAt
);
}
}
<?php
namespace Source\Gateways\Doors;
use Source\Entities\Door;
interface DoorsRepository {
/**
* Create a new door
*
* @param Door $door
* @return Door
*/
public function create(Door $door): Door;
/**
* Attempt to find a door by a token
*
* @param string $token
* @return Door|null
*/
public function getByToken(string $token): ?Door;
}
<?php
namespace Source\Gateways\Doors;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class DoorsRepositoryServiceProvider extends ServiceProvider implements DeferrableProvider {
/**
* Register any application services.
*
* @return void
*/
public function register() {
$this->app->singleton(DoorsRepository::class, static function (Application $app) {
if (env('APP_ENV') === 'memory') {
return new LocalDoorsRepository();
}
if(env('APP_ENV') === 'testing') {
return new InMemoryDoorsRepository();
}
return new DatabaseDoorsRepository();
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void {
}
/**
* @return array
*/
public function provides() {
return [DoorsRepository::class];
}
}
<?php
namespace Source\Gateways\Doors;
use Source\Entities\Door;
class InMemoryDoorsRepository implements DoorsRepository {
/** @var Door[] */
protected array $doors = [];
/**
* @inheritDoc
*/
public function create(Door $door): Door {
$this->doors[] = $door;
return $door;
}
/**
* @inheritDoc
*/
public function getByToken(string $token): ?Door {
foreach ($this->doors as $door) {
if ($door->hasTokenOf($token)) {
return $door;
}
}
return null;
}
}
<?php
namespace Source\Gateways\Doors;
use Source\Entities\Door;
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');
}
}
<?php
namespace Source\UseCases\Doors\Authenticate;
use Source\Gateways\Doors\DoorsRepository;
class Authenticate implements AuthenticateUseCase {
protected DoorsRepository $doors;
public function __construct(DoorsRepository $doors) {
$this->doors = $doors;
}
/**
* @inheritDoc
*/
public function check(Presenter $presenter, ?string $token): void {
if (!$token) {
return;
}
$found = $this->doors->getByToken($token);
$response = new ResponseModel();
$response->setDoor($found);
$presenter->present($response);
}
}
<?php
namespace Source\UseCases\Doors\Authenticate;