From 7c8e79c199400c3e4a7f71220f0581ffb37d7b1d Mon Sep 17 00:00:00 2001 From: Evann Regnault Date: Wed, 19 Jun 2024 23:41:01 +0200 Subject: [PATCH] Redis cache for faster auth --- .env.example | 2 ++ README.md | 2 ++ index.php | 6 +++++- src/Keycloak/KeycloakAuth.php | 28 +++++++++++++++++++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index df2d928..95f22f0 100644 --- a/.env.example +++ b/.env.example @@ -4,3 +4,5 @@ keycloak_token_url=https://keycloak.example.com/auth/realms/master/protocol/open realm=master users_path=/srv/dav/public base_uri=/ +redis_host=localhost +redis_port=6379 diff --git a/README.md b/README.md index 60240ee..eb5d30c 100755 --- a/README.md +++ b/README.md @@ -6,9 +6,11 @@ This project is meant to be a WebDav Server using Keycloak roles for it's [Princ ## Requirements - PHP +- [phpredis](https://github.com/phpredis/phpredis) - Composer - A running keycloak instance - A Client with `Direct access grants` enabled +- A running Redis Instance ## Configuration diff --git a/index.php b/index.php index e734816..84f4601 100755 --- a/index.php +++ b/index.php @@ -12,10 +12,14 @@ require 'vendor/autoload.php'; $dotenv = Dotenv::createImmutable(__DIR__); $dotenv->load(); + +$redis_client = new Redis(); +$redis_client->connect($_ENV['redis_host'], intval($_ENV['redis_port'])); + $principalBackend = new RolesBackend(); // Set Auth -$authBackend = new Keycloak\KeycloakAuth($principalBackend,$_ENV['client_id'], $_ENV['client_secret'], $_ENV['keycloak_token_url'] ); +$authBackend = new Keycloak\KeycloakAuth($redis_client, $principalBackend,$_ENV['client_id'], $_ENV['client_secret'], $_ENV['keycloak_token_url'] ); $authBackend->setRealm($_ENV['realm']); $authPlugin = new DAV\Auth\Plugin($authBackend); diff --git a/src/Keycloak/KeycloakAuth.php b/src/Keycloak/KeycloakAuth.php index bbd139a..21768b0 100644 --- a/src/Keycloak/KeycloakAuth.php +++ b/src/Keycloak/KeycloakAuth.php @@ -3,27 +3,49 @@ namespace Keycloak; use Principal\RolesBackend; +use Redis; use Sabre\DAV\Auth\Backend\AbstractBasic; class KeycloakAuth extends AbstractBasic { + private Redis $redis_client; private RolesBackend $principal_backend; private string $client_id; private string $client_secret; private string $keycloakTokenUrl; - public function __construct(RolesBackend $principal_backend, string $client_id, + public function __construct(Redis $redis_client, RolesBackend $principal_backend, string $client_id, string $client_secret, string $keycloakTokenUrl) { + $this->redis_client = $redis_client; $this->principal_backend = $principal_backend; $this->client_id = $client_id; $this->client_secret = $client_secret; $this->keycloakTokenUrl = $keycloakTokenUrl; } + private function addReditCache(string $username, string $hash, array $roles): void { + $this->redis_client->set("credentials".$username.$hash, json_encode($roles), ["EX" => 60 * 15]); + } + + private function checkReditCache(string $username, string $hash): array { + if (!$this->redis_client->exists("credentials" . $username . $hash)) return ["valid" => false]; + + $datastr = $this->redis_client->get("credentials" . $username . $hash); + $roles = json_decode($datastr); + return ["valid" => true, "roles" => $roles]; + } + protected function validateUserPass($username, $password) : bool { + $hash = hash("sha256", $password); + $inCache = $this->checkReditCache($username, $hash); + if ($inCache["valid"]) { + $this->principal_backend->setPrincipals($inCache["roles"]); + return true; + } + $curl = curl_init(); curl_setopt_array($curl, [ @@ -52,6 +74,8 @@ class KeycloakAuth extends AbstractBasic if ($err || $data['http_code'] != 200) { return false; } else { + $this->addReditCache($username, $hash, []); + $x = json_decode($body); $user_data = json_decode(base64_decode(explode(".",$x->access_token)[1]), true); @@ -66,6 +90,8 @@ class KeycloakAuth extends AbstractBasic $this->principal_backend->setPrincipals($roles); + $this->addReditCache($username, $hash, $roles); + return true; }