commit cac54ffa874b7ef4e2ad59d336b3cd84a2429768
parent 1100345e685fe0beb740364a3bbef165d23beef7
Author: dankert <openrat@jandankert.de>
Date: Mon, 7 Feb 2022 21:44:42 +0100
New: Authenticate API users with the HTTP authorization header.
Diffstat:
5 files changed, 78 insertions(+), 47 deletions(-)
diff --git a/modules/cms/Dispatcher.class.php b/modules/cms/Dispatcher.class.php
@@ -8,11 +8,14 @@ namespace cms;
use BadMethodCallException;
use cms\action\Action;
use cms\action\RequestParams;
+use cms\auth\Auth;
+use cms\auth\InternalAuth;
use cms\base\Configuration;
use cms\base\DB;
use cms\base\DefaultConfig;
use cms\base\Startup;
use cms\base\Version;
+use cms\model\User;
use configuration\Config;
use configuration\ConfigurationLoader;
use database\Database;
@@ -99,6 +102,7 @@ class Dispatcher
$this->connectToDatabase();
$this->startDatabaseTransaction();
+ $this->checkLogin();
try{
@@ -133,10 +137,6 @@ class Dispatcher
$result['output']['_id' ] = $this->request->id;
- // Yes, closing the session flushes the session data and unlocks other waiting requests.
- // Now another request is able to be executed.
- Session::close();
-
// Ablaufzeit für den Inhalt auf aktuelle Zeit setzen.
header('Expires: ' . substr(date('r', time() - date('Z')), 0, -5) . 'GMT', false);
@@ -144,8 +144,47 @@ class Dispatcher
}
+ /**
+ * Clear up after the work is done.
+ */
+ private function clear() {
+ // Yes, closing the session flushes the session data and unlocks other waiting requests.
+ // Now another request is able to be executed.
+ Session::close();
+ if ( $this->request->authUser ) {
+ session_destroy();
+ setcookie('or_sid','',time());
+ }
+ }
+
+
+ /**
+ * Make a authentication, if there is a HTTP authorization.
+ */
+ private function checkLogin() {
+
+ if ( $this->request->withAuthorization ) {
+ $userAuth = new InternalAuth();
+ $status = $userAuth->login( $this->request->authUser,$this->request->authPassword,'' );
+ if ( ! ($status & Auth::STATUS_SUCCESS) )
+ throw new SecurityException('user cannot be authenticated');
+
+ $user = User::loadWithName( $this->request->authUser,User::AUTH_TYPE_INTERNAL );
+ Session::setUser( $user );
+ }
+ }
+
+
+ /**
+ * checks if the request contains a pleasant CSRF token.
+ *
+ * @return void
+ */
private function checkPostToken()
{
+ if ( $this->request->withAuthorization )
+ return; // no CSRF token necessary if there are no cookies used for authentication.
+
if ( Configuration::subset('security')->is('use_post_token',true) &&
$this->request->isAction &&
$this->request->getToken() != Session::token() ) {
@@ -448,27 +487,8 @@ class Dispatcher
if ( ! $updater->isUpdateRequired( DB::get() ) )
return;
-
- if ( ! $dbConfig->is('auto_update',true))
- throw new \LogicException('DB Update required, but auto-update is disabled. '.Startup::TITLE." ".Startup::VERSION." needs DB-version ".Update::SUPPORTED_VERSION );
-
-
- try {
- $adminDb = new Database( $dbConfig->subset('admin')->getConfig() + $dbConfig->getConfig() );
- $adminDb->id = $dbid;
- } catch (\Exception $e) {
-
- throw new UIException('DATABASE_ERROR_CONNECTION', $e->getMessage(), [], $e);
- }
-
- $updater->update($adminDb);
-
- // Try to close the PDO connection. PDO doc:
- // To close the connection, you need to destroy the object by ensuring that all
- // remaining references to it are deleted—you do this by assigning NULL to the variable that holds the object.
- // If you don't do this explicitly, PHP will automatically close the connection when your script ends.
- $adminDb = null;
- unset($adminDb);
+ Logger::error("Database update required. Please call /status/?upgrade");
+ throw new LogicException("Database update required, try calling /status/?upgrade");
}
diff --git a/modules/cms/action/LoginAction.class.php b/modules/cms/action/LoginAction.class.php
@@ -3,26 +3,9 @@
namespace cms\action;
-use cms\auth\Auth;
-use cms\auth\AuthRunner;
-use cms\auth\InternalAuth;
use cms\base\Configuration;
-use cms\base\DB;
-use cms\base\Startup;
-use cms\model\User;
use configuration\Config;
-use Exception;
-use language\Messages;
-use logger\Logger;
-use openid_connect\OpenIDConnectClient;
-use security\Password;
-use util\exception\ObjectNotFoundException;
-use util\exception\SecurityException;
-use util\exception\UIException;
-use util\exception\ValidationException;
-use util\mail\Mail;
use util\Session;
-use util\text\TextMessage;
// OpenRat Content Management System
diff --git a/modules/cms/action/RequestParams.class.php b/modules/cms/action/RequestParams.class.php
@@ -2,6 +2,9 @@
namespace cms\action;
+use cms\auth\Auth;
+use cms\auth\InternalAuth;
+use util\exception\SecurityException;
use util\exception\ValidationException;
use util\json\JSON;
use util\mail\Mail;
@@ -27,6 +30,10 @@ class RequestParams
public $isAction;
+ public $headers;
+ public $authUser;
+ public $authPassword;
+
private $parameter;
/**
@@ -34,6 +41,12 @@ class RequestParams
*/
public $isUIAction;
+
+ /**
+ * @var bool
+ */
+ public $withAuthorization;
+
/**
* RequestParams constructor.
*/
@@ -41,6 +54,15 @@ class RequestParams
{
// Is this a POST request?
$this->isAction = @$_SERVER['REQUEST_METHOD'] == 'POST';
+ $this->headers = array_change_key_case(getallheaders(), CASE_LOWER);
+
+ if ( @$this->headers['authorization'] ) {
+ $this->withAuthorization = true;
+ // Only supporting Basic Auth
+ // Maybe in the future we will support JWT Bearer tokens...
+ if ( substr( $this->headers['authorization'],0,6 ) == 'Basic ' )
+ list($this->authUser,$this->authPassword) = explode(':',base64_decode( substr( $this->headers['authorization'],6) ) );
+ }
$this->setParameterStore();
@@ -55,13 +77,16 @@ class RequestParams
*/
protected function setParameterStore() {
- $headers = array_change_key_case(getallheaders(), CASE_LOWER);
- $contenttype = trim(explode( ';',@$headers['content-type'])[0]);
+ $contenttype = trim(explode( ';',@$this->headers['content-type'])[0]);
if ( !$this->isAction )
$this->parameter = &$_GET;
+ // GET method in the HTTP/1.1 spec, section 9.3:
+ // The GET method means retrieve whatever information ([...]) is identified by the Request-URI.
+ // so the request body MUST be ignored here.
else
+ // POST requests are NOT idempotent
switch( $contenttype ) {
// These content-types are known by PHP, so we do NOT have to parse them:
case 'application/x-www-form-urlencoded': // the most used form url encoding
diff --git a/modules/cms/base/Startup.class.php b/modules/cms/base/Startup.class.php
@@ -20,6 +20,7 @@ namespace cms\base;
use ErrorException;
use logger\Logger;
use util\exception\ValidationException;
+use util\Session;
class Startup {
@@ -188,8 +189,10 @@ class Startup {
return true;
// Aktuelle Datenbankverbindung ist readonly.
- $db = DB::get();
- if (isset($db->conf['readonly']) && $db->conf['readonly'])
+ //$db = DB::get();
+ $db = Session::getDatabase();
+
+ if ($db && isset($db->conf['readonly']) && $db->conf['readonly'])
return true;
return false;
diff --git a/modules/cms/model/BaseObject.class.php b/modules/cms/model/BaseObject.class.php
@@ -282,7 +282,7 @@ SQL
$sql = Db::sql( <<<SQL
SELECT * FROM {{acl}}
WHERE objectid={objectid}
- --AND ( languageid={languageid} OR languageid IS NULL )
+ /*--AND ( languageid={languageid} OR languageid IS NULL )*/
AND ( type = {user} AND userid={userid}
OR type = {group} AND $sqlGroupClause
OR type = {all}