Commit 3127edcd authored by Jacob Priddy's avatar Jacob Priddy 👌

start adding tokens

parent 03eca62f
......@@ -7,7 +7,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Token extends Model
{
protected $table = 'user_tokens';
protected $casts = [
'expires_at' => 'datetime',
];
/**
* @return BelongsTo
......
......@@ -2,34 +2,12 @@
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable {
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
'api_token',
];
use SoftDeletes;
/**
* The attributes that should be cast to native types.
......@@ -37,7 +15,7 @@ class User extends Authenticatable {
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
'expires_at' => 'datetime',
];
/**
......
......@@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserTokensTable extends Migration
class CreateTokensTable extends Migration
{
/**
* Run the migrations.
......@@ -13,7 +13,7 @@ class CreateUserTokensTable extends Migration
*/
public function up()
{
Schema::create('user_tokens', static function (Blueprint $table) {
Schema::create('tokens', static function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name')->nullable()->default(null);
$table->string('api_token');
......@@ -31,6 +31,6 @@ class CreateUserTokensTable extends Migration
*/
public function down()
{
Schema::dropIfExists('user_tokens');
Schema::dropIfExists('tokens');
}
}
......@@ -14,19 +14,21 @@ use App\Http\Controllers\UsersController;
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(
[
'prefix' => 'users',
],
static function () {
Route::get('/', [UsersController::class, 'index']);
Route::post('/', [UsersController::class, 'store']);
Route::get('{userId}', [UsersController::class, 'get']);
Route::put('{userId}', [UsersController::class, 'update']);
Route::delete('{userId}', [UsersController::class, 'delete']);
}
);
Route::group(['middleware' => 'auth:api'], static function () {
Route::group(
[
'prefix' => 'users',
],
static function () {
Route::get('/', [UsersController::class, 'index']);
Route::post('/', [UsersController::class, 'store']);
Route::get('{userId}', [UsersController::class, 'get']);
Route::put('{userId}', [UsersController::class, 'update']);
Route::delete('{userId}', [UsersController::class, 'delete']);
}
);
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
Route::get('/user', static function (Request $request) {
return $request->user();
});
});
<?php
namespace Source\Entities;
use Carbon\Carbon;
/**
* Class Token
*
* @package Source\Entities
*/
class Token {
/**
* @var int
*/
protected int $id;
/**
* @var int
*/
protected int $userId;
/**
* @var string
*/
protected string $tokenString;
/**
* @var string|null
*/
protected ?string $name;
/**
* @var Carbon|null
*/
protected ?Carbon $expiresAt;
/**
* @var Carbon|null
*/
protected ?Carbon $createdAt;
/**
* @var Carbon|null
*/
protected ?Carbon $updatedAt;
/**
* @param int $id
* @param int $userId
* @param string|null $name
* @param string $tokenString
* @param Carbon|null $expiresAt
* @param Carbon|null $createdAt
* @param Carbon|null $updatedAt
*/
public function __construct(int $id,
int $userId,
string $tokenString,
?string $name = null,
?Carbon $expiresAt = null,
?Carbon $createdAt = null,
?Carbon $updatedAt = null) {
$this->id = $id;
$this->userId = $userId;
$this->name = $name;
$this->tokenString = $tokenString;
$this->expiresAt = $expiresAt;
$this->createdAt = $createdAt;
$this->updatedAt = $updatedAt;
}
/**
* @param string|null $token
* @return bool
*/
public function matches(?string $token): bool {
return $this->tokenString === $token;
}
/**
* @return int
*/
public function getId(): int {
return $this->id;
}
/**
* @return int
*/
public function getUserId(): int {
return $this->userId;
}
/**
* @return string
*/
public function getTokenString(): string {
return $this->tokenString;
}
/**
* @return string|null
*/
public function getName(): ?string {
return $this->name;
}
/**
* @return Carbon|null
*/
public function getExpiresAt(): ?Carbon {
return $this->expiresAt;
}
/**
* @return Carbon|null
*/
public function getCreatedAt(): ?Carbon {
return $this->createdAt;
}
/**
* @return Carbon|null
*/
public function getUpdatedAt(): ?Carbon {
return $this->updatedAt;
}
}
......@@ -91,7 +91,7 @@ class User {
$this->lastName = $lastName;
$this->displayName = $displayName;
$this->emplid = $emplid;
$this->email = $email;
$this->email = strtolower($email);
$this->password = $password;
$this->doorcode = $doorcode;
$this->expiresAt = $expiresAt;
......@@ -187,7 +187,23 @@ class User {
* @param string $id
* @return bool
*/
public function hasUserIdOf(string $id): bool {
public function hasUserIdOf(?string $id): bool {
if ($id === null) {
return false;
}
return $this->getId() === (int)$id;
}
public function matchCredentials(?string $email, ?string $password): bool {
return $this->getEmail() === $email && $this->getPassword() === $password;
}
/**
* @param string $doorcode
* @return bool
*/
public function hasDoorcodeOf(?string $doorcode): bool {
return $this->getDoorcode() === $doorcode;
}
}
<?php
namespace Source\Gateways\Tokens;
use App\User;
use Source\Entities\Token;
use Source\Exceptions\EntityNotFoundException;
class DatabaseTokensRepository implements TokensRepository {
/**
* @inheritDoc
*/
public function create(Token $token): Token {
$user = User::find($token->getUserId());
if (!$user) {
throw new EntityNotFoundException('Cannot create token for non existent user');
}
return $user->tokens()->create(
[
'name' => $token->getName(),
'api_token' => $token->getTokenString(),
'expires_at' => $token->getExpiresAt(),
]
);
}
/**
* @inheritDoc
*/
public function findByToken(string $token): ?Token {
$found = \App\Token::where('api_token', $token)->first();
if (!$found) {
return null;
}
return new Token(
$found->id,
$found->user_id,
$found->name,
$found->expires_at,
$found->created_at,
$found->updated_at
);
}
}
<?php
namespace Source\Gateways\Tokens;
use Source\Entities\Token;
class InMemoryTokensRepository implements TokensRepository {
/**
* @var Token[]
*/
protected array $tokens = [];
/** @inheritDoc */
public function create(Token $token): Token {
$this->tokens[] = $token;
return $token;
}
/** @inheritDoc */
public function findByToken(string $tokenToMatch): ?Token {
foreach ($this->tokens as $token) {
if ($token->matches($tokenToMatch)) {
return $token;
}
}
return null;
}
}
<?php
namespace Source\Gateways\Tokens;
use Carbon\Carbon;
use Source\Entities\Token;
class LocalTokensRepository extends InMemoryTokensRepository {
public function __construct() {
$this->tokens[] = new Token(1, 1, 'token_string', 'basic token');
$this->tokens[] = new Token(2, 420, 'expired token', Carbon::now()->subDays(3));
}
}
<?php
namespace Source\Gateways\Tokens;
use Source\Entities\User;
use Source\Entities\Token;
use Source\Exceptions\EntityNotFoundException;
interface TokensRepository {
/**
* @param Token $token
* @return Token
* @throws EntityNotFoundException
*/
public function create(Token $token): Token;
/**
* @param string $token
* @return User|null
*/
public function findByToken(string $token): ?Token;
}
<?php
namespace Source\Gateways\Tokens;
use Source\Gateways\Users\UsersRepository;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
/**
* Service provider must be registered in AppServiceProvider
*/
class TokensRepositoryServiceProvider extends ServiceProvider implements DeferrableProvider {
/**
* Register any application services.
*
* @return void
*/
public function register() {
$this->app->singleton(TokensRepository::class, static function (Application $app) {
if (env('APP_ENV') === 'memory') {
return new LocalTokensRepository();
}
if(env('APP_ENV') === 'testing') {
return new InMemoryTokensRepository();
}
return new DatabaseTokensRepository();
});
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot(): void {
}
/**
* @return array
*/
public function provides() {
return [TokensRepository::class];
}
}
......@@ -4,5 +4,197 @@
namespace Source\Gateways\Users;
use Source\Entities\User;
class DatabaseUsersRepository implements UsersRepository {
/**
* @inheritDoc
*/
public function get(string $userId): ?User {
$user = \App\User::find($userId);
if (!$user) {
return null;
}
return new User(
$user->id,
$user->first_name,
$user->last_name,
$user->display_name,
$user->emplid,
$user->email,
$user->password,
$user->doorcode,
$user->expires_at,
$user->created_at,
$user->updated_at
);
}
/**
* @inheritDoc
*/
public function all(): array {
$users = \App\User::all();
return array_map(
static function (\App\User $user) {
return new User(
$user->id,
$user->first_name,
$user->last_name,
$user->display_name,
$user->emplid,
$user->email,
$user->password,
$user->doorcode,
$user->expires_at,
$user->created_at,
$user->updated_at
);
},
$users
);
}
/**
* @inheritDoc
*/
public function create(User $user): ?User {
$newUser = \App\User::create(
[
'first_name' => $user->getFirstName(),
'last_name' => $user->getLastName(),
'display_name' => $user->getDisplayName(),
'emplid' => $user->getEmplid(),
'email' => $user->getEmail(),
'password' => bcrypt($user->getPassword()),
'doorcode' => hash('sha256', $user->getDoorcode()),
'expires_at' =>$user->getExpiresAt(),
]
);
return new User(
$newUser->id,
$newUser->first_name,
$newUser->last_name,
$newUser->display_name,
$newUser->emplid,
$newUser->email,
$newUser->password,
$newUser->doorcode,
$newUser->expires_at,
$newUser->created_at,
$newUser->updated_at
);
}
/**
* @inheritDoc
*/
public function update(string $userId, User $user): ?User {
$dbUser = \App\User::find($userId);
if (!$dbUser) {
return null;
}
$dbUser->first_name = $user->getFirstName();
$dbUser->last_name = $user->getLastName();
$dbUser->display_name = $user->getDisplayName();
$dbUser->emplid = $user->getEmplid();
$dbUser->email = $user->getEmail();
$dbUser->password = bcrypt($user->getPassword());
$dbUser->doorcode = hash('sha256', $user->getDoorcode());
$dbUser->save();
return new User(
$dbUser->id,
$dbUser->first_name,
$dbUser->last_name,
$dbUser->display_name,
$dbUser->emplid,
$dbUser->email,
$dbUser->password,
$dbUser->doorcode,
$dbUser->expires_at,
$dbUser->created_at,
$dbUser->updated_at
);
}
/**
* @inheritDoc
*/
public function delete(string $userId): bool {
$user = \App\User::find($userId);
return $user->delete();
}
/**
* @inheritDoc
*/
public function exists(string $userId): bool {
return \App\User::find($userId) !== null;
}
/**
* @inheritDoc
*/
public function findByCredentials(