Commit 6abd38ce authored by Jacob Priddy's avatar Jacob Priddy 👌
Browse files

Add override command

parent 893d5f46
......@@ -7,6 +7,7 @@ use Source\Authorization\AuthorizerServiceProvider;
use Source\Gateways\Saml\SamlRepositoryServiceProvider;
use Source\Gateways\Doors\DoorsRepositoryServiceProvider;
use Source\Gateways\Users\UsersRepositoryServiceProvider;
use Source\UseCases\Door\Commands\CommandServiceProvider;
use Source\Gateways\Groups\GroupsRepositoryServiceProvider;
use Source\Gateways\Tokens\TokensRepositoryServiceProvider;
use Source\Gateways\Entries\EntriesRepositoryServiceProvider;
......@@ -179,6 +180,9 @@ class AppServiceProvider extends ServiceProvider
OverrideUpdateUseCaseServiceProvider::class,
OverridesForDoorUseCaseServiceProvider::class,
OverridesForDateRangeUseCaseServiceProvider::class,
// Commands
CommandServiceProvider::class,
];
/**
......
......@@ -53,9 +53,7 @@ class Access implements AccessUseCase
$this->entries = $entries;
$this->salt = $salt;
foreach ($authorizers as $authorizer) {
$this->authorizers[] = $authorizer;
}
$this->authorizers = $authorizers;
}
/**
......
......@@ -8,7 +8,9 @@ use Carbon\Carbon;
use Source\Entities\User;
use Source\Authorization\Permissions;
use Source\Authorization\ApiAuthorizer;
use Source\Exceptions\EntityNotFoundException;
use Source\UseCases\Door\Access\AccessAuthorizer;
use Source\UseCases\Door\Commands\CommandHandler;
class CommandAuthorizer implements AccessAuthorizer
{
......@@ -17,9 +19,15 @@ class CommandAuthorizer implements AccessAuthorizer
*/
protected ApiAuthorizer $authorizer;
public function __construct(ApiAuthorizer $authorizer)
/**
* @var \Source\UseCases\Door\Commands\CommandHandler
*/
protected CommandHandler $commandHandler;
public function __construct(ApiAuthorizer $authorizer, CommandHandler $commandHandler)
{
$this->authorizer = $authorizer;
$this->commandHandler = $commandHandler;
}
/**
......@@ -27,12 +35,20 @@ class CommandAuthorizer implements AccessAuthorizer
*/
public function check(?User $user, Carbon $date, string $doorId, string $doorcode, ?string $commandString): int
{
if ($commandString) {
if ($this->authorizer->allowsOne([Permissions::DOOR_COMMANDER, Permissions::MANAGE_DOORS])) {
if ($commandString && $user) {
$this->authorizer->setCurrentUserId($user->getId());
try {
if ($this->authorizer->allowsOne([Permissions::DOOR_COMMANDER, Permissions::MANAGE_DOORS]) &&
$this->commandHandler->handle($user, $doorId, $commandString)) {
return self::ALLOW;
}
} catch (EntityNotFoundException $e) {
// Do nothing and let it deny as the permissions could not be found for the user
}
return self::DENY;
}
return self::CONTINUE;
}
}
......@@ -4,7 +4,30 @@
namespace Source\UseCases\Door\Commands;
interface Command
use Source\Entities\User;
abstract class Command
{
/**
* @var string
*/
protected string $opcode;
/**
* return true on success, false on error
*
* @param \Source\Entities\User $user
* @param string $doorId
* @param string $args
* @return bool
*/
abstract public function execute(User $user, string $doorId, string $args): bool;
/**
* @return string
*/
public function getOpcode(): string
{
return $this->opcode;
}
}
......@@ -4,7 +4,17 @@
namespace Source\UseCases\Door\Commands;
use Source\Entities\User;
interface CommandHandler
{
/**
* Returns true on success, false on failure
*
* @param \Source\Entities\User $user
* @param string $doorId
* @param string $commandString
* @return bool
*/
public function handle(User $user, string $doorId, string $commandString): bool;
}
......@@ -18,7 +18,9 @@ class CommandServiceProvider extends ServiceProvider implements DeferrableProvid
public function register()
{
$this->app->bind(CommandHandler::class, static function (Application $app) {
return new DoorCommandHandler();
return new DoorCommandHandler(
$app->make(OverrideCommand::class)
);
});
}
......
......@@ -4,7 +4,42 @@
namespace Source\UseCases\Door\Commands;
class DoorCommandHandler
use Source\Entities\User;
class DoorCommandHandler implements CommandHandler
{
/**
* @var \Source\UseCases\Door\Commands\Command[]
*/
protected array $commands = [];
public function __construct(Command ...$commands)
{
foreach ($commands as $command) {
$this->commands[$command->getOpcode()] = $command;
}
}
/**
* @inheritDoc
*/
public function handle(User $user, string $doorId, string $commandString): bool
{
// Need to be able to parse OPCode
if (strlen($commandString) < 2) {
return false;
}
// First 2 digits are the command opcode
$opcode = substr($commandString, 0, 2);
$args = substr($commandString, 2);
$command = $this->commands[$opcode] ?? null;
if (!$command) {
return false;
}
return $command->execute($user, $doorId, $args);
}
}
<?php
namespace Source\UseCases\Door\Commands;
use Carbon\Carbon;
use Source\Entities\User;
use Source\Entities\Override;
use Source\Exceptions\EntityNotFoundException;
use Source\Gateways\Overrides\OverridesRepository;
class OverrideCommand extends Command
{
protected const OPEN_MODE = '1';
protected const CLOSED_MODE = '2';
protected const CLEAR_MODE = '0';
protected string $opcode = '00';
/**
* @var \Source\Gateways\Overrides\OverridesRepository
*/
protected OverridesRepository $overrides;
public function __construct(OverridesRepository $overrides)
{
$this->overrides = $overrides;
}
/**
* @inheritDoc
*/
public function execute(User $user, string $doorId, string $args): bool
{
/*
* The layout for this command is
* [type][length?]
*
* type -> one digit specifying type
* length -> time in minutes for the override to last (not required for a cancel)
*
* types:
* - 1: open mode
* - 2: closed mode
* - 0: cancel any overrides
*/
if ($args === '') {
return false;
}
switch ($args[0]) {
case self::OPEN_MODE:
$length = (int)substr($args, 1);
if ($length > 0) {
return $this->overrides->addOverride(new Override(
0,
"Open mode override created at keypad by {$user->getDisplayName()}.",
$user->getId(),
$doorId,
Override::TYPE_OPEN,
Carbon::now(),
Carbon::now()->addRealMinutes($length)
)) !== null;
}
return false;
case self::CLOSED_MODE:
$length = (int)substr($args, 1);
if ($length > 0) {
return $this->overrides->addOverride(new Override(
0,
"Closed mode override created at keypad by {$user->getDisplayName()}.",
$user->getId(),
$doorId,
Override::TYPE_LOCKED,
Carbon::now(),
Carbon::now()->addRealMinutes($length)
)) !== null;
}
return false;
case self::CLEAR_MODE:
$override = $this->overrides->activeOverrideForDoorBetween($doorId, Carbon::now(), Carbon::now());
if ($override) {
try {
$this->overrides->updateOverride($override->getId(), new Override(
$override->getId(),
$override->getReason() . "\nCancelled at keypad by {$user->getDisplayName()}.",
$override->getUserId(),
$override->getDoorId(),
$override->getType(),
$override->getStart(),
Carbon::now()->subSecond()
));
} catch (EntityNotFoundException $e) {
return false;
}
}
return true;
default:
// Unrecognized type
return false;
}
}
}
Supports Markdown
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