More security logic

This commit is contained in:
2025-03-14 18:48:30 +01:00
parent 3e7cfbccb1
commit 5a91d41f1d
5 changed files with 84 additions and 14 deletions

15
TODO.md
View File

@@ -4,3 +4,18 @@
- [ ] JWT authentication
- [ ] Access control on groups, slots and props
- [ ] Token authentication
- [ ] HTTPS support
- [ ] Handle remaining CLI options
- [ ] Message bus
- [ ] Slot update notifications
- [ ] Slot creation and deletion
- [ ] Group update notifications
- [ ] Group creation and deletion
- [ ] Group join/leave
- [ ] Triggers
- [ ] Listen for messages on bus to trigger events
- [ ] Dynamic trigger management (post, delete)
- [ ] Schemas
- [ ] Apply schemas to group
- [ ] Apply schemas to slot
- [ ] Check schemas on update

View File

@@ -18,6 +18,7 @@ use SlotDb\SlotDb\Data\Group\Group;
use SlotDb\SlotDb\Data\Schema\Schema;
use SlotDb\SlotDb\Data\Slot\Slot;
use SlotDb\SlotDb\Data\Slot\SlotRepository;
use SlotDb\SlotDb\Security\AccessChecker;
class Daemon
{
@@ -57,8 +58,10 @@ class Daemon
$this->messageBus = new MessageBus();
$accessChecker = new AccessChecker();
$routes = new RouteCollection();
$routes->addController(new Http\Controller\SlotsController($em->getRepository(Slot::class), $this->messageBus));
$routes->addController(new Http\Controller\SlotsController($em->getRepository(Slot::class), $this->messageBus, $accessChecker));
$routes->addController(new Http\Controller\GroupsController($em->getRepository(Group::class), $em->getRepository(Slot::class)));
$routes->addController(new Http\Controller\SchemassController($em->getRepository(Schema::class)));

View File

@@ -9,7 +9,7 @@ use SlotDb\SlotDb\Bus\MessageBus;
use SlotDb\SlotDb\Data\Slot\Slot;
use SlotDb\SlotDb\Data\Slot\SlotNotFoundException;
use SlotDb\SlotDb\Data\Slot\SlotRepository;
use SlotDb\SlotDb\Security\AccessChecker;
class SlotsController extends Controller
{
@@ -17,6 +17,7 @@ class SlotsController extends Controller
public function __construct(
private readonly SlotRepository $slots,
private readonly MessageBus $bus,
private readonly AccessChecker $accessChecker,
)
{
@@ -60,6 +61,7 @@ class SlotsController extends Controller
#[Route(path:"/api/slotdb/v1/slots", methods:["POST"])]
public function createSlots(ServerRequestInterface $request)
{
$data = json_decode($request->getBody());
$slots = [];
foreach ($data as $item) {
@@ -87,6 +89,8 @@ class SlotsController extends Controller
return Response::json([ 'error'=>"Slot not found" ])->withStatus(404);
}
$acl = $request->getAttribute("acl");
return Response::json($slotObj);
}

View File

@@ -9,11 +9,57 @@ use React\Promise\PromiseInterface;
class SecurityMiddleware
{
private bool $useJwt = false;
private bool $useToken = false;
public function __construct(
private readonly ?string $jwtSecret = null,
private readonly ?string $jwtClaim = "slotdb",
private readonly ?string $tokenFile = null,
)
{
}
public function __invoke(ServerRequestInterface $request, ?callable $next = null)
{
if (!$this->useJwt && !$this->useToken) {
// No authentication
$request = $request->withAttribute("acl", [ "*/rw"]);
} else {
if ($this->useJwt && $request->hasHeader('authorization')) {
$auth = $request->getHeader('authorization')[0];
[$type,$token] = explode(" ", $auth, 2);
$acl = $this->checkJwt($token);
if ($acl === null) {
return Response::json(['error'=>'Unauthorized'])->withStatus(Response::STATUS_UNAUTHORIZED);
}
$request = $request->withAttribute("acl", $acl);
} elseif ($this->useToken && $request->hasHeader('x-token')) {
$token = $request->getHeader('x-token')[0];
$acl = $this->checkToken($token);
if ($acl === null) {
return Response::json(['error'=>'Unauthorized'])->withStatus(Response::STATUS_UNAUTHORIZED);
}
$request = $request->withAttribute("acl", $acl);
}
}
$response = $next($request);
return $response;
}
private function checkJwt(string $token): ?array
{
return null;
}
private function checkToken(string $token): ?array
{
return null;
}
}

View File

@@ -32,16 +32,18 @@ function print_usage(): never {
-v,--verbose Include debug level logging to STDOUT
-V,--version Print version and exit
-l,--listen ADDR Set listen address for HTTP requests (env: SLOTDB_LISTEN)
-c,--config FILE Read configuration from file (env: SLOTDB_CONFIG)
--db URI Use custom database connection URI (env: SLOTDB_DATABASE)
--init-db Initialize database schema
--upgrade-db Upgrade database schema
--root FILE Set root CA certificate for SSL (env: SLOTDB_SSL_ROOT)
--cert FILE Set certificate for SSL (env: SLOTDB_SSL_CERT)
--key FILE Set private key file for SSL (env: SLOTDB_SSL_KEY)
--jwt-secret SECRET Use JWT auth with secret (env: SLOTDB_JWT_SECRET)
--jwt-claim CLAIM Claim key to use for matching properties (env: SLOTDB_JWT_CLAIM)
-c,--config FILE ¤ Read configuration from file (env: SLOTDB_CONFIG)
--db URI ¤ Use custom database connection URI (env: SLOTDB_DATABASE)
--init-db ¤ Initialize database schema
--upgrade-db ¤ Upgrade database schema
--root FILE ¤ Set root CA certificate for SSL (env: SLOTDB_SSL_ROOT)
--cert FILE ¤ Set certificate for SSL (env: SLOTDB_SSL_CERT)
--key FILE ¤ Set private key file for SSL (env: SLOTDB_SSL_KEY)
--jwt-secret SECRET ¤ Use JWT auth with secret (env: SLOTDB_JWT_SECRET)
--jwt-claim CLAIM ¤ Key in JWT claims that contains ACLs (env: SLOTDB_JWT_CLAIM)
--tokens FILE ¤ Read access tokens from file (env: SLOTDB_TOKENS_FILE)
--http-log FILE Log HTTP requests to file (env: SLOTDB_HTTP_LOG)
¤=not yet implemented
Defaults:
HTTP listen address: 0.0.0.0:8080
@@ -52,11 +54,11 @@ function print_usage(): never {
The key pointed to by the claim option should be a string or an array of
strings, with each string granting access to a group, slot or property.
"*#*:*/r" Read all groups, all slots, all properties
"*#*.*/r" Read all groups, all slots, all properties
"first#/rw" Read-write everything in the group first
"slot*/rw" Read-write all slots starting with 'slot' in all groups
"slota,slotb/w" Write-only access to slots 'slota' and 'slotb'
"*:available" Read-only access to the 'available' prop on all slots
"slot[ab]/w" Write-only access to slots 'slota' and 'slotb'
"*.available" Read-only access to the 'available' prop on all slots
Examples:
SLOTDB_LISTEN=0.0.0.0:9876 slotdb