Commit cec13886 authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Merge branch '38-door-group-relationships' into 'master'

Resolve "Door Group Relationships"

Closes #38

See merge request kretschmar/doorcode!31
parents df7a0801 a6372250
Pipeline #5565 passed with stages
in 3 minutes and 53 seconds
......@@ -96,9 +96,15 @@ $config = [
'preston.carman:I am young and hip' => [
'first_name' => ['Preston'],
'last_name' => ['Carman'],
'emplid' => ['69420'],
'emplid' => ['6942000'],
'email' => ['Preston.Carman@wallawalla.edu']
],
'admin:admin' => [
'first_name' => ['Sheev'],
'last_name' => ['Palpatine'],
'emplid' => ['order66'],
'email' => ['sithL0rd@senate.com']
]
],
......
......@@ -3,10 +3,27 @@
namespace App;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Door extends Authenticatable
{
protected $fillable = ['id', 'name', 'location', 'created_at', 'updated_at'];
protected $guarded = [];
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function groups(): BelongsToMany
{
return $this->belongsToMany(Group::class);
}
public static function boot()
{
parent::boot();
static::deleting(static function (Door $door) {
// Detach all groups
$door->groups()->detach();
});
}
}
......@@ -16,12 +16,21 @@ class Group extends Model
return $this->belongsToMany(User::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function doors(): BelongsToMany
{
return $this->belongsToMany(Door::class);
}
public static function boot()
{
parent::boot();
static::deleting(static function (Group $group) {
$group->users()->detach();
$group->doors()->detach();
});
}
}
......@@ -9,12 +9,18 @@ use Source\UseCases\Doors\CreateDoor\CreateDoorUseCase;
use Source\UseCases\Doors\DeleteDoor\DeleteDoorUseCase;
use Source\UseCases\Doors\UpdateDoor\UpdateDoorUseCase;
use Source\UseCases\Doors\GetAllDoors\GetAllDoorsUseCase;
use Source\UseCases\DoorGroup\GetDoorGroups\GetDoorGroupsUseCase;
use Source\UseCases\DoorGroup\AddDoorToGroup\AddDoorToGroupUseCase;
use Source\UseCases\Doors\GenerateDoorToken\GenerateDoorTokenUseCase;
use Source\UseCases\Doors\GetDoor\APIPresenter as GetDoorAPIPresenter;
use Source\UseCases\Doors\CreateDoor\APIPresenter as CreateDoorAPIPresenter;
use Source\UseCases\Doors\DeleteDoor\APIPresenter as DeleteDoorAPIPresenter;
use Source\UseCases\Doors\UpdateDoor\APIPresenter as UpdateDoorAPIPresenter;
use Source\UseCases\DoorGroup\RemoveDoorFromGroup\RemoveDoorFromGroupUseCase;
use Source\UseCases\Doors\GetAllDoors\APIPresenter as GetAllDoorsAPIPresenter;
use Source\UseCases\DoorGroup\GetDoorGroups\APIPresenter as GetDoorGroupsAPIPresenter;
use Source\UseCases\DoorGroup\AddDoorToGroup\APIPresenter as AddDoorToGroupAPIPresenter;
use Source\UseCases\DoorGroup\RemoveDoorFromGroup\APIPresenter as RemoveDoorFromGroupAPIPresenter;
class DoorsController extends ApiController
{
......@@ -26,7 +32,7 @@ class DoorsController extends ApiController
*/
public function index(GetAllDoorsUseCase $getDoors): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new GetAllDoorsAPIPresenter();
......@@ -44,7 +50,7 @@ class DoorsController extends ApiController
*/
public function get(GetDoorUseCase $getDoor, string $doorId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new GetDoorAPIPresenter();
......@@ -63,7 +69,7 @@ class DoorsController extends ApiController
*/
public function store(CreateDoorUseCase $createDoor): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$this->validate($this->request, [
'location' => 'required|string|max:255',
......@@ -89,7 +95,7 @@ class DoorsController extends ApiController
*/
public function update(UpdateDoorUseCase $updateDoor, string $doorId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$this->validate($this->request, [
'location' => 'string|max:255',
......@@ -113,7 +119,7 @@ class DoorsController extends ApiController
*/
public function regenerateToken(GenerateDoorTokenUseCase $tokenGenerator, string $doorId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new CreateDoorAPIPresenter();
......@@ -131,7 +137,7 @@ class DoorsController extends ApiController
*/
public function delete(DeleteDoorUseCase $doorDelyeeter, string $doorId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_DOORS]);
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new DeleteDoorAPIPresenter();
......@@ -139,4 +145,60 @@ class DoorsController extends ApiController
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\DoorGroup\GetDoorGroups\GetDoorGroupsUseCase $doorGroups
* @param string $doorId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function getGroupsForDoor(GetDoorGroupsUseCase $doorGroups, string $doorId): JsonResponse
{
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new GetDoorGroupsAPIPresenter();
$doorGroups->getGroupsForDoor($doorId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\DoorGroup\AddDoorToGroup\AddDoorToGroupUseCase $addDoorToGroup
* @param string $doorId
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function addDoorToGroup(AddDoorToGroupUseCase $addDoorToGroup, string $doorId, string $groupId): JsonResponse
{
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new AddDoorToGroupAPIPresenter();
$addDoorToGroup->addDoorToGroup($doorId, $groupId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\DoorGroup\RemoveDoorFromGroup\RemoveDoorFromGroupUseCase $removeDoorFromGroup
* @param string $doorId
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function removeDoorFromGroup(RemoveDoorFromGroupUseCase $removeDoorFromGroup, string $doorId, string $groupId): JsonResponse
{
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new RemoveDoorFromGroupAPIPresenter();
$removeDoorFromGroup->removeDoorFromGroup($doorId, $groupId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
}
......@@ -10,12 +10,14 @@ use Source\UseCases\Groups\CreateGroup\CreateGroupUseCase;
use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCase;
use Source\UseCases\Groups\UpdateGroup\UpdateGroupUseCase;
use Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCase;
use Source\UseCases\DoorGroup\GetGroupDoors\GetGroupDoorsUseCase;
use Source\UseCases\GroupUser\GetGroupUsers\GetGroupUsersUseCase;
use Source\UseCases\Groups\GetGroup\APIPresenter as GetGroupAPIPresenter;
use Source\UseCases\Groups\GetAllGroups\APIPresenter as AllGroupsAPIPresenter;
use Source\UseCases\Groups\CreateGroup\APIPresenter as CreateGroupAPIPresenter;
use Source\UseCases\Groups\DeleteGroup\APIPresenter as DeleteGroupAPIPresenter;
use Source\UseCases\Groups\UpdateGroup\APIPresenter as UpdateGroupAPIPresenter;
use Source\UseCases\DoorGroup\GetGroupDoors\APIPresenter as GetGroupDoorsAPIPresenter;
use Source\UseCases\GroupUser\GetGroupUsers\APIPresenter as GetGroupUsersAPIPresenter;
class GroupsController extends ApiController
......@@ -30,7 +32,7 @@ class GroupsController extends ApiController
*/
public function store(CreateGroupUseCase $useCase): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$this->authorizer->protect(Permissions::MANAGE_GROUPS);
$this->validate($this->request, [
'title' => 'required|string|max:255',
......@@ -53,7 +55,7 @@ class GroupsController extends ApiController
*/
public function delete(DeleteGroupUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$this->authorizer->protect(Permissions::MANAGE_GROUPS);
$presenter = new DeleteGroupAPIPresenter();
......@@ -111,7 +113,7 @@ class GroupsController extends ApiController
*/
public function update(UpdateGroupUseCase $useCase, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_GROUPS]);
$this->authorizer->protect(Permissions::MANAGE_GROUPS);
$this->validate($this->request, [
'title' => 'required|string|max:255',
......@@ -146,4 +148,22 @@ class GroupsController extends ApiController
return $this->respondWithData($presenter->getViewModel());
}
/**
* @param \Source\UseCases\DoorGroup\GetGroupDoors\GetGroupDoorsUseCase $groupDoors
* @param string $groupId
* @return \Illuminate\Http\JsonResponse
* @throws \Source\Exceptions\AuthorizationException
* @throws \Source\Exceptions\EntityNotFoundException
*/
public function getDoorsForGroup(GetGroupDoorsUseCase $groupDoors, string $groupId): JsonResponse
{
$this->authorizer->protect(Permissions::MANAGE_DOORS);
$presenter = new GetGroupDoorsAPIPresenter();
$groupDoors->getDoorsForGroup($groupId, $presenter);
return $this->respondWithData($presenter->getViewModel());
}
}
......@@ -33,7 +33,7 @@ class UsersController extends ApiController
*/
public function index(GetAllUsersUseCase $getAllUsers): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new AllUsersAPIPresenter();
......@@ -51,7 +51,7 @@ class UsersController extends ApiController
*/
public function get(GetUserUseCase $getUser, string $userId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new GetUserAPIPresenter();
......@@ -70,7 +70,7 @@ class UsersController extends ApiController
*/
public function store(CreateUserUseCase $createUser): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$this->validate($this->request, [
'first_name' => 'required|string|max:255',
......@@ -104,7 +104,7 @@ class UsersController extends ApiController
*/
public function update(UpdateUserUseCase $updateUser, string $userId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$this->validate($this->request, [
'first_name' => 'string|max:255',
......@@ -141,7 +141,7 @@ class UsersController extends ApiController
*/
public function delete(DeleteUserUseCase $deleteUser, string $userId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new DeleteUserAPIPresenter();
......@@ -165,7 +165,7 @@ class UsersController extends ApiController
*/
public function addUserToGroup(AddUserToGroupUseCase $useCase, string $userId, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new AddUserToGroupAPIPresenter();
......@@ -184,7 +184,7 @@ class UsersController extends ApiController
*/
public function removeUserFromGroup(RemoveUserFromGroupUseCase $useCase, string $userId, string $groupId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new RemoveUserFromGroupAPIPresenter();
......@@ -202,7 +202,7 @@ class UsersController extends ApiController
*/
public function getGroupsForUser(GetUserGroupsUseCase $useCase, string $userId): JsonResponse
{
$this->authorizer->protectAll([Permissions::MANAGE_USERS]);
$this->authorizer->protect(Permissions::MANAGE_USERS);
$presenter = new GetUserGroupsAPIPresenter();
......
......@@ -11,6 +11,7 @@ use Source\Gateways\Groups\GroupsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\UseCases\Doors\GetDoor\GetDoorUseCaseServiceProvider;
use Source\UseCases\Users\GetUser\GetUserUseCaseServiceProvider;
use Source\Gateways\DoorGroup\DoorGroupRepositoryServiceProvider;
use Source\Gateways\GroupUser\GroupUserRepositoryServiceProvider;
use Source\UseCases\Groups\GetGroup\GetGroupUseCaseServiceProvider;
use Source\UseCases\Doors\CreateDoor\CreateDoorUseCaseServiceProvider;
......@@ -26,10 +27,14 @@ use Source\UseCases\Groups\DeleteGroup\DeleteGroupUseCaseServiceProvider;
use Source\UseCases\Groups\UpdateGroup\UpdateGroupUseCaseServiceProvider;
use Source\UseCases\Token\Authenticate\AuthenticateUseCaseServiceProvider;
use Source\UseCases\Groups\GetAllGroups\GetAllGroupsUseCaseServiceProvider;
use Source\UseCases\DoorGroup\GetDoorGroups\GetDoorGroupsUseCaseServiceProvider;
use Source\UseCases\DoorGroup\GetGroupDoors\GetGroupDoorsUseCaseServiceProvider;
use Source\UseCases\GroupUser\GetGroupUsers\GetGroupUsersUseCaseServiceProvider;
use Source\UseCases\GroupUser\GetUserGroups\GetUserGroupsUseCaseServiceProvider;
use Source\UseCases\DoorGroup\AddDoorToGroup\AddDoorToGroupUseCaseServiceProvider;
use Source\UseCases\GroupUser\AddUserToGroup\AddUserToGroupUseCaseServiceProvider;
use Source\UseCases\Doors\GenerateDoorToken\GenerateDoorTokenUseCaseServiceProvider;
use Source\UseCases\DoorGroup\RemoveDoorFromGroup\RemoveDoorFromGroupUseCaseServiceProvider;
use Source\UseCases\GroupUser\RemoveUserFromGroup\RemoveUserFromGroupUseCaseServiceProvider;
use Source\UseCases\Doors\Authenticate\AuthenticateUseCaseServiceProvider as DoorAuthenticateUseCaseServiceProvider;
use Source\UseCases\Users\Authenticate\AuthenticateUseCaseServiceProvider as UserAuthenticateUseCaseServiceProvider;
......@@ -46,6 +51,7 @@ class AppServiceProvider extends ServiceProvider
DoorsRepositoryServiceProvider::class,
TokensRepositoryServiceProvider::class,
GroupsRepositoryServiceProvider::class,
DoorGroupRepositoryServiceProvider::class,
GroupUserRepositoryServiceProvider::class,
];
......@@ -73,6 +79,12 @@ class AppServiceProvider extends ServiceProvider
AddUserToGroupUseCaseServiceProvider::class,
RemoveUserFromGroupUseCaseServiceProvider::class,
// DoorGroup
GetDoorGroupsUseCaseServiceProvider::class,
GetGroupDoorsUseCaseServiceProvider::class,
AddDoorToGroupUseCaseServiceProvider::class,
RemoveDoorFromGroupUseCaseServiceProvider::class,
// Doors
GetDoorUseCaseServiceProvider::class,
CreateDoorUseCaseServiceProvider::class,
......
......@@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateGroupDoorTable extends Migration
class CreateDoorGroupTable extends Migration
{
/**
* Run the migrations.
......@@ -14,13 +14,12 @@ class CreateGroupDoorTable extends Migration
public function up()
{
// This table maps the permissions a specific group has to a specific door
Schema::create('group_door', function (Blueprint $table) {
Schema::create('door_group', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('door_id');
$table->foreign('door_id')->references('id')->on('doors');
$table->unsignedBigInteger('group_id');
$table->foreign('group_id')->references('id')->on('groups');
$table->boolean('open_mode_allowed')->default(false);
$table->timestamps();
});
}
......@@ -32,6 +31,6 @@ class CreateGroupDoorTable extends Migration
*/
public function down()
{
Schema::dropIfExists('group_door');
Schema::dropIfExists('door_group');
}
}
......@@ -32,7 +32,6 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::delete('{userId}', [UsersController::class, 'delete']);
Route::get('{userId}/groups', [UsersController::class, 'getGroupsForUser']);
Route::post('{userId}/group/{groupId}', [UsersController::class, 'addUserToGroup']);
Route::delete('{userId}/group/{groupId}', [UsersController::class, 'removeUserFromGroup']);
});
......@@ -47,6 +46,7 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::delete('{groupId}', [GroupsController::class, 'delete']);
Route::get('{groupId}/users', [GroupsController::class, 'getUsersForGroup']);
Route::get('{groupId}/doors', [GroupsController::class, 'getDoorsForGroup']);
});
Route::group([
......@@ -58,6 +58,10 @@ Route::group(['middleware' => 'auth:api'], static function () {
Route::put('{doorId}', [DoorsController::class, 'update']);
Route::post('/{doorId}/regenerate-token', [DoorsController::class, 'regenerateToken']);
Route::delete('{doorId}', [DoorsController::class, 'delete']);
Route::get('{doorId}/groups', [DoorsController::class, 'getGroupsForDoor']);
Route::post('{doorId}/group/{groupId}', [DoorsController::class, 'addDoorToGroup']);
Route::delete('{doorId}/group/{groupId}', [DoorsController::class, 'removeDoorFromGroup']);
});
Route::group([
......
......@@ -4,6 +4,8 @@
namespace Source\Authorization;
use Illuminate\Contracts\Auth\Guard;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Groups\GroupsRepository;
use Source\Exceptions\AuthorizationException;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\GroupUser\GroupUserRepository;
......@@ -20,9 +22,25 @@ class ApiAuthorizer implements Authorizer
*/
protected GroupUserRepository $groupUserRepository;
public function __construct(Guard $guard, GroupUserRepository $groupUserRepository)
{
/**
* @var \Source\Gateways\Groups\GroupsRepository
*/
protected GroupsRepository $groups;
/**
* @var \Source\Gateways\Users\UsersRepository
*/
protected UsersRepository $users;
public function __construct(
Guard $guard,
UsersRepository $users,
GroupsRepository $groups,
GroupUserRepository $groupUserRepository
) {
$this->users = $users;
$this->guard = $guard;
$this->groups = $groups;
$this->groupUserRepository = $groupUserRepository;
}
......@@ -139,7 +157,7 @@ class ApiAuthorizer implements Authorizer
*/
public function protectAdminRights(string $userId, ?string $groupId = null): void
{
$user = $this->groupUserRepository->getUser($userId);
$user = $this->users->get($userId);
if (!$user) {
throw new EntityNotFoundException('User not found.');
......@@ -153,7 +171,7 @@ class ApiAuthorizer implements Authorizer
}
if ($groupId) {
$group = $this->groupUserRepository->getGroup($groupId);
$group = $this->groups->get($groupId);
if (!$group) {
throw new EntityNotFoundException('Group not found.');
}
......
......@@ -6,6 +6,8 @@ namespace Source\Authorization;
use App\Guards\ApiGuard;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
use Source\Gateways\Users\UsersRepository;
use Source\Gateways\Groups\GroupsRepository;
use Source\Gateways\GroupUser\GroupUserRepository;
use Illuminate\Contracts\Support\DeferrableProvider;
......@@ -22,7 +24,12 @@ class AuthorizerServiceProvider extends ServiceProvider implements DeferrablePro
public function register()
{
$this->app->singleton(Authorizer::class, static function (Application $app) {
return new ApiAuthorizer($app->make(ApiGuard::class), $app->make(GroupUserRepository::class));
return new ApiAuthorizer(
$app->make(ApiGuard::class),
$app->make(UsersRepository::class),
$app->make(GroupsRepository::class),
$app->make(GroupUserRepository::class)
);
});
}
......
<?php
namespace Source\Gateways\DoorGroup;
use App\Door;
use App\Group;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Doors\DatabaseDoorsRepository;
use Source\Gateways\Groups\DatabaseGroupsRepository;
class DatabaseDoorGroupRepository implements DoorGroupRepository
{
/**
* @inheritDoc
*/
public function getGroupsForDoor(string $doorId): array
{
/** @var Door|null $door */
$door = Door::find((int)$doorId);
if (!$door) {
throw new EntityNotFoundException();
}
return array_map(static function (Group $group) {
DatabaseGroupsRepository::makeGroupFromDbGroup($group);
}, $door->groups()->get()->values()->all());
}
/**
* @inheritDoc
*/
public function getDoorsForGroup(string $groupId): array
{
/** @var Group|null $group */
$group = Group::find((int)$groupId);
if (!$group) {
throw new EntityNotFoundException();
}
return array_map(static function (Door $door) {
return DatabaseDoorsRepository::makeDoorFromDb($door);
}, $group->doors()->get()->values()->all());
}
/**
* @inheritDoc
*/
public function addDoorToGroup(string $doorId, string $groupId): void
{
/** @var Door|null $door */
$door = Door::find((int)$doorId);
/** @var Group|null $group */
$group = Group::find((int)$groupId);
if (!$door) {
throw new EntityNotFoundException('Door not found.');
}
if (!$group) {
throw new EntityNotFoundException('Group not found.');
}
$door->groups()->attach($groupId);
}
/**
* @inheritDoc
*/
public function removeDoorFromGroup(string $doorId, string $groupId): void
{
/** @var Door|null $door */
$door = Door::find((int)$doorId);
/** @var Group|null $group */
$group = Group::find((int)$groupId);
if (!$door) {
throw new EntityNotFoundException('Door not found.');
}<