From d73a89c616c645afa86729f9c4e2e91c2c66cfa2 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 12:50:49 +0200 Subject: [PATCH 1/7] do not treat Basic authentication as a token, but just skip it --- restapi/index.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/restapi/index.php b/restapi/index.php index 808c45f8a..c09d199a6 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -3071,7 +3071,12 @@ class RestapiAuthMiddleware implements MiddlewareInterface { /* {{{ */ } /** - * Example middleware invokable class + * Auth middleware invokable class + * + * This methods checks for an api token in the Authorization header or + * a valid session in the cookie `mydms_session`. + * It does not support Basic authentication. It actually treats that + * as a wrong api token and authentication fails. * * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request * @param \Psr\Http\Server\RequestHandlerInterface $handler @@ -3133,6 +3138,8 @@ class RestapiAuthMiddleware implements MiddlewareInterface { /* {{{ */ $userobj = null; // $logger->log(var_export($environment, true), PEAR_LOG_DEBUG); if(!empty($environment['HTTP_AUTHORIZATION']) && !empty($settings->_apiKey) && !empty($settings->_apiUserId)) { + /* We cannot handle Basic authentication, so skip it */ + if (substr($environment['HTTP_AUTHORIZATION'], 0, 6) != 'Basic ') { $logger->log("Authorization key: ".$environment['HTTP_AUTHORIZATION'], PEAR_LOG_DEBUG); if($settings->_apiKey == $environment['HTTP_AUTHORIZATION']) { if(!($userobj = $dms->getUser($settings->_apiUserId))) { @@ -3160,6 +3167,9 @@ class RestapiAuthMiddleware implements MiddlewareInterface { /* {{{ */ return $response; } $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + } else { + $logger->log("Login with Basic auth cannot be handled by AuthMiddleware", PEAR_LOG_INFO); + } } else { $logger->log("Checking for valid session", PEAR_LOG_INFO); require_once("../inc/inc.ClassSession.php"); From 2f668328fab9a339f69fd95622490c77ffacaddd Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 12:51:10 +0200 Subject: [PATCH 2/7] add middleware for basic authentication --- inc/inc.ClassAuthenticationMiddleware.php | 78 ++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/inc/inc.ClassAuthenticationMiddleware.php b/inc/inc.ClassAuthenticationMiddleware.php index 97e74c55f..63cf98914 100644 --- a/inc/inc.ClassAuthenticationMiddleware.php +++ b/inc/inc.ClassAuthenticationMiddleware.php @@ -63,7 +63,7 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ return $response; } - $logger->log("Invoke middleware for method " . $request->getMethod() . " on '" . $request->getUri()->getPath() . "'", PEAR_LOG_INFO); + $logger->log("Invoke AuthSessionMiddleware for method " . $request->getMethod() . " on '" . $request->getUri()->getPath() . "'", PEAR_LOG_INFO); require_once("inc/inc.ClassSession.php"); $session = new SeedDMS_Session($dms->getDb()); if (isset($_COOKIE["mydms_session"])) { @@ -106,3 +106,79 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ return $response; } } /* }}} */ + +/** + * Middleware for authentication based on basic authentication + * + **/ +class SeedDMS_Auth_Middleware_Basic { /* {{{ */ + + private $container; + + public function __construct($container) { + $this->container = $container; + } + + /** + * Basic authentication middleware invokable class + * + * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request + * @param \Psr\Http\Message\ResponseInterface $response PSR7 response + * @param callable $next Next middleware + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke($request, $handler) { + $dms = $this->container->get('dms'); + $settings = $this->container->get('config'); + $logger = $this->container->get('logger'); + $userobj = null; + if ($this->container->has('userobj')) { + $userobj = $this->container->get('userobj'); + } + + if ($userobj) { + $response = $handler->handle($request); + return $response; + } + + $logger->log("Invoke AuthBasicMiddleware for method " . $request->getMethod() . " on '" . $request->getUri()->getPath() . "'", PEAR_LOG_INFO); + $environment = $request->getServerParams(); + if(!empty($environment['HTTP_AUTHORIZATION'])) { + $tmp = explode(' ', $environment['HTTP_AUTHORIZATION'], 2); + switch($tmp[0]) { + case 'Basic': + $logger->log("Basic authentication with ".$tmp[0]."=".$tmp[1], PEAR_LOG_INFO); + $authenticator = $this->container->get('authenticator'); + $kk = explode(':', base64_decode($tmp[1])); + $userobj = $authenticator->authenticate($kk[0], $kk[1]); + if(!$userobj) { + $logger->log("Login with basic authentication for '".$kk[0]."' failed", PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); + return $response->withStatus(403); + } + $dms->setUser($userobj); + if($this->container instanceof \Slim\Container) + $this->container['userobj'] = $userobj; + else + $this->container->set('userobj', $userobj); + $logger->log("Login with basic authentication as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + break; + } + } + $this->container->set('userobj', $userobj); + + if(!$userobj) + $logger->log("Not yet authenticated. Pass on to next middleware", PEAR_LOG_INFO); + else + $logger->log("Authenticated as ".(is_object($userobj) ? $userobj->getLogin() : "annon").". Pass on to next middleware", PEAR_LOG_INFO); + + /* Always pass on to the next middleware. If that middleware does + * authentication, then it should first check if 'userobj' in the container + * is already set. The authentication shipped with seeddms restapi does that + * and skips its own authentication, if userobj already exists. + */ + $response = $handler->handle($request); + return $response; + } +} /* }}} */ From e175858235782aaefd4a7550e86c46a3940edd87 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 12:55:35 +0200 Subject: [PATCH 3/7] add groups and categories to 'statstotal' --- restapi/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/restapi/index.php b/restapi/index.php index c09d199a6..a2d255285 100644 --- a/restapi/index.php +++ b/restapi/index.php @@ -3002,7 +3002,7 @@ final class SeedDMS_RestapiController { /* {{{ */ return $check; $data = []; - foreach (array('docstotal', 'folderstotal', 'userstotal') as $type) { + foreach (array('docstotal', 'folderstotal', 'userstotal', 'groupstotal', 'categoriestotal') as $type) { $total = $dms->getStatisticalData($type); $data[$type] = $total; } From afbb891bd54b0af9c1c414479dabff2b1683e53f Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 12:57:03 +0200 Subject: [PATCH 4/7] add changes of 5.1.43 --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 4113939e0..973d4d13a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ -------------------------------------------------------------------------------- Changes in version 5.1.43 -------------------------------------------------------------------------------- +- add Slim middleware for Basic authentication (can be used by extensions) -------------------------------------------------------------------------------- Changes in version 5.1.42 From 46e927621c26b0e95729f480d02d188e9ff53ee5 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 12:58:03 +0200 Subject: [PATCH 5/7] add changes of 5.1.43 --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 973d4d13a..2346c1531 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Changes in version 5.1.43 -------------------------------------------------------------------------------- - add Slim middleware for Basic authentication (can be used by extensions) +- rest api endpoint 'statstotal' returns number of groups and categories -------------------------------------------------------------------------------- Changes in version 5.1.42 From 66ab0f25d34c62b4c10be46d8a1a1e152760796c Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 13:59:59 +0200 Subject: [PATCH 6/7] pass response factory to SessionAuth Middleware --- inc/inc.ClassAuthenticationMiddleware.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/inc/inc.ClassAuthenticationMiddleware.php b/inc/inc.ClassAuthenticationMiddleware.php index 63cf98914..bd9b1c40d 100644 --- a/inc/inc.ClassAuthenticationMiddleware.php +++ b/inc/inc.ClassAuthenticationMiddleware.php @@ -35,8 +35,11 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ private $container; - public function __construct($container) { + private $responsefactory; + + public function __construct($container, $responsefactory) { $this->container = $container; + $this->responsefactory = $responsefactory; } /** @@ -73,6 +76,7 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ /* Delete Cookie */ setcookie("mydms_session", $dms_session, time() - 3600, $settings->_httpRoot); $logger->log("Session for id '" . $dms_session . "' has gone", PEAR_LOG_ERR); + $response = $this->responsefactory->createResponse(); return $response->withStatus(403); } @@ -83,6 +87,7 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ setcookie("mydms_session", $dms_session, time() - 3600, $settings->_httpRoot); if ($settings->_enableGuestLogin) { if (!($userobj = $dms->getUser($settings->_guestID))) { + $response = $this->responsefactory->createResponse(); return $response->withStatus(403); } } else { @@ -92,12 +97,14 @@ class SeedDMS_Auth_Middleware_Session { /* {{{ */ if ($userobj->isAdmin()) { if ($resArr["su"]) { if (!($userobj = $dms->getUser($resArr["su"]))) { + $response = $this->responsefactory->createResponse(); return $response->withStatus(403); } } } $dms->setUser($userobj); } else { + $response = $this->responsefactory->createResponse(); return $response->withStatus(403); } $this->container->set('userobj', $userobj); From 6db475692aa980eea777eb32d25f063c5fd8a562 Mon Sep 17 00:00:00 2001 From: Uwe Steinmann Date: Fri, 24 Oct 2025 14:00:42 +0200 Subject: [PATCH 7/7] new token based middleware --- inc/inc.ClassAuthenticationMiddleware.php | 97 ++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/inc/inc.ClassAuthenticationMiddleware.php b/inc/inc.ClassAuthenticationMiddleware.php index bd9b1c40d..f406747cf 100644 --- a/inc/inc.ClassAuthenticationMiddleware.php +++ b/inc/inc.ClassAuthenticationMiddleware.php @@ -122,8 +122,11 @@ class SeedDMS_Auth_Middleware_Basic { /* {{{ */ private $container; - public function __construct($container) { + private $responsefactory; + + public function __construct($container, $responsefactory) { $this->container = $container; + $this->responsefactory = $responsefactory; } /** @@ -189,3 +192,95 @@ class SeedDMS_Auth_Middleware_Basic { /* {{{ */ return $response; } } /* }}} */ + +/** + * Middleware for authentication based on token + * + **/ +class SeedDMS_Auth_Middleware_Token { /* {{{ */ + + private $container; + + private $responsefactory; + + public function __construct($container, $responsefactory) { + $this->container = $container; + $this->responsefactory = $responsefactory; + } + + /** + * Basic authentication middleware invokable class + * + * @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request + * @param \Psr\Http\Message\ResponseInterface $response PSR7 response + * @param callable $next Next middleware + * + * @return \Psr\Http\Message\ResponseInterface + */ + public function __invoke($request, $handler) { + $dms = $this->container->get('dms'); + $settings = $this->container->get('config'); + $logger = $this->container->get('logger'); + $userobj = null; + if ($this->container->has('userobj')) { + $userobj = $this->container->get('userobj'); + } + + if ($userobj) { + $response = $handler->handle($request); + return $response; + } + + $logger->log("Invoke AuthTokenMiddleware for method " . $request->getMethod() . " on '" . $request->getUri()->getPath() . "'", PEAR_LOG_INFO); + $environment = $request->getServerParams(); + /* Do not even try to authenticate if HTTP_AUTHORIZATION is empty, contains + * a ' ' (in case of Basic authentication), the api key is not set, the api + * user is not set. + */ + if (!empty($environment['HTTP_AUTHORIZATION']) && strstr($environment['HTTP_AUTHORIZATION'], ' ') === false && !empty($settings->_apiKey) && !empty($settings->_apiUserId)) { + $logger->log("Authorization key: ".$environment['HTTP_AUTHORIZATION'], PEAR_LOG_DEBUG); + if($settings->_apiKey == $environment['HTTP_AUTHORIZATION']) { + if(!($userobj = $dms->getUser($settings->_apiUserId))) { + $response = $this->responsefactory->createResponse(); + $response = $response->withHeader('Content-Type', 'application/json'); + $response = $response->withStatus(403); + $response->getBody()->write( + (string)json_encode( + ['success'=>false, 'message'=>'Invalid user associated with api key', 'data'=>''], + JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR + ) + ); + return $response; + } + } else { + $response = $this->responsefactory->createResponse(); + $response = $response->withHeader('Content-Type', 'application/json'); + $response = $response->withStatus(403); + $response->getBody()->write( + (string)json_encode( + ['success'=>false, 'message'=>'Wrong api key', 'data'=>''], + JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR + ) + ); + return $response; + } + $logger->log("Login with apikey as '".$userobj->getLogin()."' successful", PEAR_LOG_INFO); + } + + $this->container->set('userobj', $userobj); + + if(!$userobj) + $logger->log("Not yet authenticated. Pass on to next middleware", PEAR_LOG_INFO); + else + $logger->log("Authenticated as ".(is_object($userobj) ? $userobj->getLogin() : "annon").". Pass on to next middleware", PEAR_LOG_INFO); + + /* Always pass on to the next middleware. If that middleware does + * authentication, then it should first check if 'userobj' in the container + * is already set. The authentication shipped with seeddms restapi does that + * and skips its own authentication, if userobj already exists. + */ + $response = $handler->handle($request); + return $response; + } +} /* }}} */ +