Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Guardians of the Kretschmar Elock System
Doorcode
Commits
893d5f46
Commit
893d5f46
authored
May 17, 2020
by
Jacob Priddy
👌
Browse files
Rework door access authorizer and start on door commands
parent
75e0714e
Changes
22
Hide whitespace changes
Inline
Side-by-side
src/web/backend/database/seeds/GroupsSeeder.php
View file @
893d5f46
...
...
@@ -29,5 +29,7 @@ class GroupsSeeder extends Seeder
$groups
->
create
(
LocalGroupsRepository
::
getManageGroupsGroup
());
$groups
->
create
(
LocalGroupsRepository
::
getLogViewGroup
());
$groups
->
create
(
LocalGroupsRepository
::
getDoorCommanderGroup
());
}
}
src/web/backend/src/Authorization/Permissions.php
View file @
893d5f46
...
...
@@ -12,4 +12,5 @@ class Permissions
public
const
CODE_QUERY
=
'code-query'
;
public
const
CURRENT_USER
=
'current-user'
;
public
const
LOGS_READ
=
'logs-read'
;
public
const
DOOR_COMMANDER
=
'door-commander'
;
}
src/web/backend/src/Gateways/Groups/LocalGroupsRepository.php
View file @
893d5f46
...
...
@@ -25,6 +25,8 @@ class LocalGroupsRepository extends InMemoryGroupsRepository
$this
->
create
(
static
::
getManageGroupsGroup
());
$this
->
create
(
static
::
getLogViewGroup
());
$this
->
create
(
static
::
getDoorCommanderGroup
());
}
/**
...
...
@@ -116,4 +118,13 @@ class LocalGroupsRepository extends InMemoryGroupsRepository
'Gives permission to view logs.'
);
}
public
static
function
getDoorCommanderGroup
():
Group
{
return
new
Group
(
11
,
Permissions
::
DOOR_COMMANDER
,
'Gives permission to enter commands at doors and to enter a door at any time'
);
}
}
src/web/backend/src/UseCases/Door/Access/Access.php
View file @
893d5f46
...
...
@@ -3,43 +3,22 @@
namespace
Source\UseCases\Door\Access
;
use
Carbon\Carbon
;
use
Source\Entities\User
;
use
Source\Entities\Entry
;
use
Source\Entities\Attempt
;
use
Source\Sanitize\CastsTo
;
use
Source\Entities\HashedSearchable
;
use
Source\Gateways\Users\UsersRepository
;
use
Source\Exceptions\AuthorizationException
;
use
Source\Exceptions\AuthenticationException
;
use
Source\Gateways\Entries\EntriesRepository
;
use
Source\Gateways\Attempts\AttemptsRepository
;
use
Source\UseCases\DoorUserGroup\DoorUserGroupMapUseCase
;
use
Source\UseCases\Door\Access\Authorizers\DoorOpenModeCheck\DoorOpenModeCheck
;
use
Source\UseCases\Door\Access\Authorizers\DoorOverrideCheck\DoorOverrideCheck
;
use
Source\UseCases\Door\Access\Authorizers\UserDoorcodeCheck\UserDoorcodeCheck
;
use
Source\UseCases\Door\Access\Authorizers\GroupScheduleCheck\GroupScheduleCheck
;
class
Access
implements
AccessUseCase
{
public
const
COMMAND_DELIMITER
=
'*'
;
use
CastsTo
;
/**
* @var \Source\UseCases\Door\Access\Authorizers\DoorOpenModeCheck\DoorOpenModeCheck
*/
protected
DoorOpenModeCheck
$openModeCheck
;
/**
* @var \Source\UseCases\Door\Access\Authorizers\UserDoorcodeCheck\UserDoorcodeCheck
*/
protected
UserDoorcodeCheck
$userCheck
;
/**
* @var \Source\UseCases\Door\Access\Authorizers\GroupScheduleCheck\GroupScheduleCheck
*/
protected
GroupScheduleCheck
$scheduleCheck
;
/**
* @var \Source\UseCases\DoorUserGroup\DoorUserGroupMapUseCase
*/
protected
DoorUserGroupMapUseCase
$doorUserMapper
;
/**
* @var \Source\Gateways\Attempts\AttemptsRepository
*/
...
...
@@ -51,28 +30,39 @@ class Access implements AccessUseCase
protected
EntriesRepository
$entries
;
/**
* @var
\Source\UseCases\Door\
Access
\
Authorizer
s\DoorOverrideCheck\DoorOverrideCheck
* @var AccessAuthorizer
[]
*/
protected
DoorOverrideCheck
$overrideCheck
;
protected
array
$authorizers
=
[];
/**
* @var \Source\Gateways\Users\UsersRepository
*/
protected
UsersRepository
$users
;
protected
string
$salt
;
public
function
__construct
(
DoorOpenModeCheck
$openModeCheck
,
UserDoorcodeCheck
$userCheck
,
GroupScheduleCheck
$scheduleCheck
,
DoorUserGroupMapUseCase
$doorUserMapper
,
DoorOverrideCheck
$overrideCheck
,
UsersRepository
$users
,
AttemptsRepository
$attempts
,
EntriesRepository
$entries
EntriesRepository
$entries
,
string
$salt
,
AccessAuthorizer
...
$authorizers
)
{
$this
->
openModeCheck
=
$openModeCheck
;
$this
->
userCheck
=
$userCheck
;
$this
->
scheduleCheck
=
$scheduleCheck
;
$this
->
doorUserMapper
=
$doorUserMapper
;
$this
->
users
=
$users
;
$this
->
attempts
=
$attempts
;
$this
->
entries
=
$entries
;
$this
->
overrideCheck
=
$overrideCheck
;
$this
->
salt
=
$salt
;
foreach
(
$authorizers
as
$authorizer
)
{
$this
->
authorizers
[]
=
$authorizer
;
}
}
/**
* @param string $doorId
* @param string $userId
* @param bool $success
*/
protected
function
logUserAccess
(
string
$doorId
,
string
$userId
,
bool
$success
):
void
{
$this
->
entries
->
add
(
new
Entry
(
...
...
@@ -83,18 +73,46 @@ class Access implements AccessUseCase
));
}
/**
* @param string $doorId
*/
protected
function
logDoorAttempt
(
string
$doorId
):
void
{
$this
->
attempts
->
add
(
new
Attempt
(
0
,
$this
->
castToInt
(
$doorId
)));
}
/**
* @param \Source\Entities\User|null $user
* @param string $doorId
*/
protected
function
logAllow
(
?User
$user
,
string
$doorId
):
void
{
if
(
$user
)
{
$this
->
logUserAccess
(
$doorId
,
$user
->
getId
(),
true
);
}
}
/**
* @param \Source\Entities\User|null $user
* @param string $doorId
*/
protected
function
logDeny
(
?User
$user
,
string
$doorId
):
void
{
if
(
$user
)
{
$this
->
logUserAccess
(
$doorId
,
$user
->
getId
(),
false
);
}
else
{
$this
->
logDoorAttempt
(
$doorId
);
}
}
/**
* @inheritDoc
*/
public
function
protectUserDoorAccess
(
?string
$doorId
,
?
string
$doorcode
):
void
public
function
protectUserDoorAccess
AtTime
(
?string
$doorId
,
string
$doorcode
,
Carbon
$date
):
void
{
/*
* Our job here is to find out if a user has access given a doorcode and the id of the door they want to access
* as well as process any sent commands
*
* This process is a little complicated as we not only have to check whether the user has access to the door
* but also if the door might possibly be allowing everyone (open mode), or if the user has access to the door
...
...
@@ -106,89 +124,38 @@ class Access implements AccessUseCase
* |
* schedule
*
* Checks (action):
* 1. Check doorId
* - door (continue)
* - no door (deny)
* 2. Check for door overrides
* - door in open mode (allow)
* - door in closed mode (deny)
* - no override (continue)
* 3. Check for open mode on the door
* - open mode on (allow)
* - open mode off (continue)
* 4. Get user from door code
* - user exists (continue)
* - no user found (deny and log attempt)
* 5. Get group intersection of doors and users
* 6. Foreach group check all user access schedules
* - schedule has currently active event (allow and log entry)
* - schedule doe snot have currently active event (continue)
* 7. Now all allowing conditions hit (deny and log entry)
* I have broken this process into small chunks that can easily be enabled and disabled, or changed
* These authorizers are injected in the service provider, and we will go until allowed or denied
*/
if
(
!
$doorId
)
{
throw
new
AuthenticationException
();
}
$now
=
Carbon
::
now
();
/*
* Check overrides
*/
$overrideStatus
=
$this
->
overrideCheck
->
checkForOverrides
(
$doorId
,
$now
);
if
(
$overrideStatus
===
DoorOverrideCheck
::
OPEN
)
{
return
;
}
if
(
$overrideStatus
===
DoorOverrideCheck
::
CLOSED
)
{
throw
new
AuthorizationException
();
}
/*
* Check for open mode
*/
if
(
$this
->
openModeCheck
->
checkOpenMode
(
$doorId
,
$now
))
{
// No logging needed to be done as we are in open mode. Just return without throwing any exceptions
return
;
}
/*
* Check for user
*/
if
(
!
(
$user
=
$this
->
userCheck
->
checkDoorCode
(
$doorcode
)))
{
$this
->
logDoorAttempt
(
$doorId
);
throw
new
AuthorizationException
();
}
$userId
=
$user
->
getId
();
$groups
=
$this
->
doorUserMapper
->
getGroupsForDoorUserIntersection
(
$doorId
,
$userId
);
/*
* Could also check if any groups exist first if wanted here.
* Wanted if not doing schedule checking
* Don't currently need this as no groups in the schedule checking will not allow as there are no groups
* to check schedule for
*/
// if (!$groups) {
// // Log that the user does not have access, but tried to access the door
// $this->logUserAccess($doorId, $userId, false);
// throw new AuthorizationException();
// }
/*
* Check for user schedule
*/
if
(
$this
->
scheduleCheck
->
checkScheduleForGroups
(
$groups
,
$now
))
{
// Log the successful entry
$this
->
logUserAccess
(
$doorId
,
$userId
,
true
);
return
;
$parts
=
explode
(
self
::
COMMAND_DELIMITER
,
$doorcode
);
$code
=
$parts
[
0
]
??
''
;
$command
=
$parts
[
1
]
??
null
;
$user
=
$this
->
users
->
findByDoorcode
(
HashedSearchable
::
hash
(
$this
->
salt
,
$code
));
foreach
(
$this
->
authorizers
as
$authorizer
)
{
switch
(
$authorizer
->
check
(
$user
,
$date
,
$doorId
,
$code
,
$command
))
{
case
AccessAuthorizer
::
ALLOW
:
$this
->
logAllow
(
$user
,
$doorId
);
return
;
case
AccessAuthorizer
::
DENY
:
$this
->
logDeny
(
$user
,
$doorId
);
throw
new
AuthorizationException
();
case
AccessAuthorizer
::
CONTINUE
:
// Fall through to default.
default
:
break
;
}
}
//
Log that the user does not have access, but tried to access the door
$this
->
log
UserAccess
(
$doorId
,
$userId
,
false
);
//
Default to deny if no authorizer allowed us
$this
->
log
Deny
(
$user
,
$doorId
);
throw
new
AuthorizationException
();
}
}
src/web/backend/src/UseCases/Door/Access/AccessAuthorizer.php
0 → 100644
View file @
893d5f46
<?php
namespace
Source\UseCases\Door\Access
;
use
Carbon\Carbon
;
use
Source\Entities\User
;
interface
AccessAuthorizer
{
public
const
ALLOW
=
0
;
public
const
DENY
=
1
;
public
const
CONTINUE
=
2
;
/**
* Must return an access authorizer status
*
* @param \Source\Entities\User|null $user
* @param \Carbon\Carbon $date
* @param string $doorId
* @param string $doorcode
* @param string $commandString
* @return int
*/
public
function
check
(
?User
$user
,
Carbon
$date
,
string
$doorId
,
string
$doorcode
,
?string
$commandString
):
int
;
}
src/web/backend/src/UseCases/Door/Access/AccessUseCase.php
View file @
893d5f46
...
...
@@ -3,13 +3,16 @@
namespace
Source\UseCases\Door\Access
;
use
Carbon\Carbon
;
interface
AccessUseCase
{
/**
* @param string|null $doorId
* @param string|null $doorcode
* @param string|null $doorId
* @param string|null $doorcode
* @param \Carbon\Carbon $date
* @throws \Source\Exceptions\AuthenticationException
* @throws \Source\Exceptions\AuthorizationException
*/
public
function
protectUserDoorAccess
(
?string
$doorId
,
?
string
$doorcode
):
void
;
public
function
protectUserDoorAccess
AtTime
(
?string
$doorId
,
string
$doorcode
,
Carbon
$date
):
void
;
}
src/web/backend/src/UseCases/Door/Access/AccessUseCaseServiceProvider.php
View file @
893d5f46
...
...
@@ -8,20 +8,12 @@ use Source\Gateways\Users\UsersRepository;
use
Source\Gateways\Entries\EntriesRepository
;
use
Illuminate\Contracts\Foundation\Application
;
use
Source\Gateways\Attempts\AttemptsRepository
;
use
Source\Gateways\Overrides\OverridesRepository
;
use
Source\Gateways\Schedules\SchedulesRepository
;
use
Illuminate\Contracts\Support\DeferrableProvider
;
use
Source\Gateways\DoorSchedule\DoorScheduleRepository
;
use
Source\Gateways\RecurrenceSet\RecurrenceSetRepository
;
use
Source\UseCases\DoorUserGroup\DoorUserGroupMapUseCase
;
use
Source\UseCases\Door\Access\Authorizers\DoorOpenModeCheck\DoorOpenModeCheck
;
use
Source\UseCases\Door\Access\Authorizers\DoorOverrideCheck\DoorOverrideCheck
;
use
Source\UseCases\Door\Access\Authorizers\UserDoorcodeCheck\UserDoorcodeCheck
;
use
Source\UseCases\Door\Access\Authorizers\GroupScheduleCheck\GroupScheduleCheck
;
use
Source\UseCases\Door\Access\Authorizers\DoorOpenModeCheck\DefaultDoorOpenModeCheck
;
use
Source\UseCases\Door\Access\Authorizers\DoorOverrideCheck\DefaultDoorOverrideCheck
;
use
Source\UseCases\Door\Access\Authorizers\UserDoorcodeCheck\DefaultUserDoorcodeCheck
;
use
Source\UseCases\Door\Access\Authorizers\GroupScheduleCheck\DefaultGroupScheduleCheck
;
use
Source\UseCases\Door\Access\Authorizers\CommandAuthorizer
;
use
Source\UseCases\Door\Access\Authorizers\OverrideAuthorizer
;
use
Source\UseCases\Door\Access\Authorizers\OpenModeAuthorizer
;
use
Source\UseCases\Door\Access\Authorizers\ScheduleAuthorizer
;
use
Source\UseCases\Door\Access\Authorizers\CommanderAuthorizer
;
/**
* Service provider must be registered in AppServiceProvider
...
...
@@ -35,40 +27,26 @@ class AccessUseCaseServiceProvider extends ServiceProvider implements Deferrable
*/
public
function
register
()
{
$this
->
app
->
bind
(
DoorOpenModeCheck
::
class
,
static
function
(
Application
$app
)
{
return
new
DefaultDoorOpenModeCheck
(
$app
->
make
(
DoorScheduleRepository
::
class
),
$app
->
make
(
RecurrenceSetRepository
::
class
)
);
});
$this
->
app
->
bind
(
GroupScheduleCheck
::
class
,
static
function
(
Application
$app
)
{
return
new
DefaultGroupScheduleCheck
(
$app
->
make
(
SchedulesRepository
::
class
),
$app
->
make
(
RecurrenceSetRepository
::
class
)
);
});
$this
->
app
->
bind
(
UserDoorcodeCheck
::
class
,
static
function
(
Application
$app
)
{
return
new
DefaultUserDoorcodeCheck
(
config
(
'app.key'
),
$app
->
make
(
UsersRepository
::
class
)
);
});
$this
->
app
->
bind
(
DoorOverrideCheck
::
class
,
static
function
(
Application
$app
)
{
return
new
DefaultDoorOverrideCheck
(
$app
->
make
(
OverridesRepository
::
class
));
});
$this
->
app
->
bind
(
AccessUseCase
::
class
,
static
function
(
Application
$app
)
{
return
new
Access
(
$app
->
make
(
DoorOpenModeCheck
::
class
),
$app
->
make
(
UserDoorcodeCheck
::
class
),
$app
->
make
(
GroupScheduleCheck
::
class
),
$app
->
make
(
DoorUserGroupMapUseCase
::
class
),
$app
->
make
(
DoorOverrideCheck
::
class
),
$app
->
make
(
UsersRepository
::
class
),
$app
->
make
(
AttemptsRepository
::
class
),
$app
->
make
(
EntriesRepository
::
class
)
$app
->
make
(
EntriesRepository
::
class
),
config
(
'app.key'
),
/*
* Order matters here because override will deny for high level groups as it assumes groups are
* checked first. Also command can deny for somebody allowed even when door is in open mode to deny cmd:
* Command check
* High level group Check
* Override check
* Open mode schedule check
* User schedule check
*/
$app
->
make
(
CommandAuthorizer
::
class
),
$app
->
make
(
CommanderAuthorizer
::
class
),
$app
->
make
(
OverrideAuthorizer
::
class
),
$app
->
make
(
OpenModeAuthorizer
::
class
),
$app
->
make
(
ScheduleAuthorizer
::
class
)
);
});
}
...
...
@@ -87,12 +65,6 @@ class AccessUseCaseServiceProvider extends ServiceProvider implements Deferrable
*/
public
function
provides
()
{
return
[
AccessUseCase
::
class
,
DoorOpenModeCheck
::
class
,
GroupScheduleCheck
::
class
,
UserDoorcodeCheck
::
class
,
DoorOverrideCheck
::
class
,
];
return
[
AccessUseCase
::
class
];
}
}
src/web/backend/src/UseCases/Door/Access/Authorizers/CommandAuthorizer.php
0 → 100644
View file @
893d5f46
<?php
namespace
Source\UseCases\Door\Access\Authorizers
;
use
Carbon\Carbon
;
use
Source\Entities\User
;
use
Source\Authorization\Permissions
;
use
Source\Authorization\ApiAuthorizer
;
use
Source\UseCases\Door\Access\AccessAuthorizer
;
class
CommandAuthorizer
implements
AccessAuthorizer
{
/**
* @var \Source\Authorization\ApiAuthorizer
*/
protected
ApiAuthorizer
$authorizer
;
public
function
__construct
(
ApiAuthorizer
$authorizer
)
{
$this
->
authorizer
=
$authorizer
;
}
/**
* @inheritDoc
*/
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
]))
{
}
return
self
::
DENY
;
}
return
self
::
CONTINUE
;
}
}
src/web/backend/src/UseCases/Door/Access/Authorizers/CommanderAuthorizer.php
0 → 100644
View file @
893d5f46
<?php
namespace
Source\UseCases\Door\Access\Authorizers
;
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
;
class
CommanderAuthorizer
implements
AccessAuthorizer
{
/**
* @var \Source\Authorization\ApiAuthorizer
*/
protected
ApiAuthorizer
$authorizer
;
public
function
__construct
(
ApiAuthorizer
$authorizer
)
{
$this
->
authorizer
=
$authorizer
;
}
/**
* @inheritDoc
*/
public
function
check
(
?User
$user
,
Carbon
$date
,
string
$doorId
,
string
$doorcode
,
?string
$commandString
):
int
{
if
(
$user
)
{
$this
->
authorizer
->
setCurrentUserId
(
$user
->
getId
());
try
{
if
(
$this
->
authorizer
->
allowsOne
([
Permissions
::
DOOR_COMMANDER
,
Permissions
::
MANAGE_DOORS
]))
{
return
self
::
ALLOW
;
}
}
catch
(
EntityNotFoundException
$e
)
{
// Keep calm and continue on
}
}
return
self
::
CONTINUE
;
}
}
src/web/backend/src/UseCases/Door/Access/Authorizers/DoorOpenModeCheck/DoorOpenModeCheck.php
deleted
100644 → 0
View file @
75e0714e
<?php
namespace
Source\UseCases\Door\Access\Authorizers\DoorOpenModeCheck
;
use
Carbon\Carbon
;
interface
DoorOpenModeCheck
{
/**
* Checks for the door to be in open mode at a specified date.
* Returns false if it is not in open mode
* Returns true if the door is in open mode and should be allowed
*
* @param string $doorId
* @param \Carbon\Carbon $date
* @return bool
*/
public
function
checkOpenMode
(
string
$doorId
,
Carbon
$date
):
bool
;
}
src/web/backend/src/UseCases/Door/Access/Authorizers/DoorOverrideCheck/DoorOverrideCheck.php
deleted
100644 → 0
View file @
75e0714e
<?php
namespace
Source\UseCases\Door\Access\Authorizers\DoorOverrideCheck
;
use
Carbon\Carbon
;
interface
DoorOverrideCheck
{
public
const
OPEN
=
0
;
public
const
CLOSED
=
1
;
public
const
NO_ACTION
=
2
;
/**
* Returns one of the constants defined on DoorOverrideCheck
*
* @param string $doorId
* @param \Carbon\Carbon $date
* @return mixed
*/
public
function
checkForOverrides
(
string
$doorId
,
Carbon
$date
):
int
;
}
src/web/backend/src/UseCases/Door/Access/Authorizers/GroupScheduleCheck/DefaultGroupScheduleCheck.php
deleted
100644 → 0
View file @
75e0714e
<?php
namespace
Source\UseCases\Door\Access\Authorizers\GroupScheduleCheck
;
use
Carbon\Carbon
;
use
InvalidArgumentException
;
use
Source\Entities\Schedule
;
use
Source\Gateways\Schedules\SchedulesRepository
;
use
Source\Gateways\RecurrenceSet\RecurrenceSetRepository
;
class
DefaultGroupScheduleCheck
implements
GroupScheduleCheck