Move ExAuth, FKOAuth1 & FKOAuthDataStore to own namespace `Friendica\Security`
authorPhilipp <admin@philipp.info>
Wed, 30 Sep 2020 09:14:01 +0000 (11:14 +0200)
committerPhilipp <admin@philipp.info>
Wed, 30 Sep 2020 09:14:01 +0000 (11:14 +0200)
17 files changed:
bin/auth_ejabberd.php
library/OAuth1.php
mod/item.php
mod/photos.php
mod/videos.php
src/Model/Attach.php
src/Model/Photo.php
src/Module/Profile/Status.php
src/Network/FKOAuth1.php [deleted file]
src/Network/FKOAuthDataStore.php [deleted file]
src/Object/Thread.php
src/Security/ExAuth.php [new file with mode: 0644]
src/Security/FKOAuth1.php [new file with mode: 0644]
src/Security/FKOAuthDataStore.php [new file with mode: 0644]
src/Security/Security.php [new file with mode: 0644]
src/Util/ExAuth.php [deleted file]
src/Util/Security.php [deleted file]

index e921829..d6e20df 100755 (executable)
@@ -58,7 +58,7 @@ if (php_sapi_name() !== 'cli') {
 
 use Dice\Dice;
 use Friendica\App\Mode;
-use Friendica\Util\ExAuth;
+use Friendica\Security\ExAuth;
 use Psr\Log\LoggerInterface;
 
 if (sizeof($_SERVER["argv"]) == 0) {
index 813234b..05ec5aa 100644 (file)
@@ -4,7 +4,7 @@
 /* Generic exception class
  */
 
-use Friendica\Network\FKOAuthDataStore;
+use Friendica\Security\FKOAuthDataStore;
 
 if (!class_exists('OAuthException', false)) {
        class OAuthException extends Exception
index 17f6486..b77e725 100644 (file)
@@ -54,7 +54,7 @@ use Friendica\Object\EMail\ItemCCEMail;
 use Friendica\Protocol\Activity;
 use Friendica\Protocol\Diaspora;
 use Friendica\Util\DateTimeFormat;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 use Friendica\Util\Strings;
 use Friendica\Worker\Delivery;
 
index 3b2fa0c..bba12aa 100644 (file)
@@ -47,7 +47,7 @@ use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Images;
 use Friendica\Util\Map;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 use Friendica\Util\Strings;
 use Friendica\Util\Temporal;
 use Friendica\Util\XML;
index 3dd1717..1ba566e 100644 (file)
@@ -33,7 +33,7 @@ use Friendica\Model\Item;
 use Friendica\Model\Profile;
 use Friendica\Model\User;
 use Friendica\Module\BaseProfile;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 
 function videos_init(App $a)
 {
index ad587e6..b81c387 100644 (file)
@@ -28,7 +28,7 @@ use Friendica\DI;
 use Friendica\Object\Image;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Mimetype;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 
 /**
  * Class to handle attach dabatase table
index f09e88c..6380f42 100644 (file)
@@ -31,7 +31,7 @@ use Friendica\Model\Storage\SystemResource;
 use Friendica\Object\Image;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Images;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 use Friendica\Util\Strings;
 
 require_once "include/dba.php";
index 421c8ac..fbc287e 100644 (file)
@@ -36,7 +36,7 @@ use Friendica\Module\BaseProfile;
 use Friendica\Module\Security\Login;
 use Friendica\Network\HTTPException;
 use Friendica\Util\DateTimeFormat;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 use Friendica\Util\Strings;
 use Friendica\Util\XML;
 
diff --git a/src/Network/FKOAuth1.php b/src/Network/FKOAuth1.php
deleted file mode 100644 (file)
index 9833d5e..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Network;
-
-use Friendica\Core\Logger;
-use Friendica\Database\DBA;
-use Friendica\DI;
-use OAuthServer;
-use OAuthSignatureMethod_HMAC_SHA1;
-use OAuthSignatureMethod_PLAINTEXT;
-
-/**
- * OAuth protocol
- */
-class FKOAuth1 extends OAuthServer
-{
-       /**
-        * Constructor
-        */
-       public function __construct()
-       {
-               parent::__construct(new FKOAuthDataStore());
-               $this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
-               $this->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());
-       }
-
-       /**
-        * @param string $uid user id
-        * @return void
-        * @throws HTTPException\ForbiddenException
-        * @throws HTTPException\InternalServerErrorException
-        */
-       public function loginUser($uid)
-       {
-               Logger::notice("FKOAuth1::loginUser $uid");
-               $a = DI::app();
-               $record = DBA::selectFirst('user', [], ['uid' => $uid, 'blocked' => 0, 'account_expired' => 0, 'account_removed' => 0, 'verified' => 1]);
-
-               if (!DBA::isResult($record)) {
-                       Logger::info('FKOAuth1::loginUser failure', ['server' => $_SERVER]);
-                       header('HTTP/1.0 401 Unauthorized');
-                       die('This api requires login');
-               }
-
-               DI::auth()->setForUser($a, $record, true);
-       }
-}
diff --git a/src/Network/FKOAuthDataStore.php b/src/Network/FKOAuthDataStore.php
deleted file mode 100644 (file)
index ee9a709..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Network;
-
-use Friendica\Core\Logger;
-use Friendica\Database\DBA;
-use Friendica\DI;
-use Friendica\Util\Strings;
-use OAuthConsumer;
-use OAuthDataStore;
-use OAuthToken;
-
-define('REQUEST_TOKEN_DURATION', 300);
-define('ACCESS_TOKEN_DURATION', 31536000);
-
-/**
- * OAuthDataStore class
- */
-class FKOAuthDataStore extends OAuthDataStore
-{
-       /**
-        * @return string
-        * @throws \Exception
-        */
-       private static function genToken()
-       {
-               return Strings::getRandomHex(32);
-       }
-
-       /**
-        * @param string $consumer_key key
-        * @return OAuthConsumer|null
-        * @throws \Exception
-        */
-       public function lookup_consumer($consumer_key)
-       {
-               Logger::log(__function__ . ":" . $consumer_key);
-
-               $s = DBA::select('clients', ['client_id', 'pw', 'redirect_uri'], ['client_id' => $consumer_key]);
-               $r = DBA::toArray($s);
-
-               if (DBA::isResult($r)) {
-                       return new OAuthConsumer($r[0]['client_id'], $r[0]['pw'], $r[0]['redirect_uri']);
-               }
-
-               return null;
-       }
-
-       /**
-        * @param OAuthConsumer $consumer
-        * @param string        $token_type
-        * @param string        $token_id
-        * @return OAuthToken|null
-        * @throws \Exception
-        */
-       public function lookup_token(OAuthConsumer $consumer, $token_type, $token_id)
-       {
-               Logger::log(__function__ . ":" . $consumer . ", " . $token_type . ", " . $token_id);
-
-               $s = DBA::select('tokens', ['id', 'secret', 'scope', 'expires', 'uid'], ['client_id' => $consumer->key, 'scope' => $token_type, 'id' => $token_id]);
-               $r = DBA::toArray($s);
-
-               if (DBA::isResult($r)) {
-                       $ot = new OAuthToken($r[0]['id'], $r[0]['secret']);
-                       $ot->scope = $r[0]['scope'];
-                       $ot->expires = $r[0]['expires'];
-                       $ot->uid = $r[0]['uid'];
-                       return $ot;
-               }
-
-               return null;
-       }
-
-       /**
-        * @param OAuthConsumer $consumer
-        * @param OAuthToken    $token
-        * @param string        $nonce
-        * @param int           $timestamp
-        * @return mixed
-        * @throws \Exception
-        */
-       public function lookup_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
-       {
-               $token = DBA::selectFirst('tokens', ['id', 'secret'], ['client_id' => $consumer->key, 'id' => $nonce, 'expires' => $timestamp]);
-               if (DBA::isResult($token)) {
-                       return new OAuthToken($token['id'], $token['secret']);
-               }
-
-               return null;
-       }
-
-       /**
-        * @param OAuthConsumer $consumer
-        * @param string        $callback
-        * @return OAuthToken|null
-        * @throws \Exception
-        */
-       public function new_request_token(OAuthConsumer $consumer, $callback = null)
-       {
-               Logger::log(__function__ . ":" . $consumer . ", " . $callback);
-               $key = self::genToken();
-               $sec = self::genToken();
-
-               if ($consumer->key) {
-                       $k = $consumer->key;
-               } else {
-                       $k = $consumer;
-               }
-
-               $r = DBA::insert(
-                       'tokens',
-                       [
-                               'id' => $key,
-                               'secret' => $sec,
-                               'client_id' => $k,
-                               'scope' => 'request',
-                               'expires' => time() + REQUEST_TOKEN_DURATION
-                       ]
-               );
-
-               if (!$r) {
-                       return null;
-               }
-
-               return new OAuthToken($key, $sec);
-       }
-
-       /**
-        * @param OAuthToken    $token    token
-        * @param OAuthConsumer $consumer consumer
-        * @param string        $verifier optional, defult null
-        * @return OAuthToken
-        * @throws \Exception
-        */
-       public function new_access_token(OAuthToken $token, OAuthConsumer $consumer, $verifier = null)
-       {
-               Logger::log(__function__ . ":" . $token . ", " . $consumer . ", " . $verifier);
-
-               // return a new access token attached to this consumer
-               // for the user associated with this token if the request token
-               // is authorized
-               // should also invalidate the request token
-
-               $ret = null;
-
-               // get user for this verifier
-               $uverifier = DI::config()->get("oauth", $verifier);
-               Logger::log(__function__ . ":" . $verifier . "," . $uverifier);
-
-               if (is_null($verifier) || ($uverifier !== false)) {
-                       $key = self::genToken();
-                       $sec = self::genToken();
-                       $r = DBA::insert(
-                               'tokens',
-                               [
-                                       'id' => $key,
-                                       'secret' => $sec,
-                                       'client_id' => $consumer->key,
-                                       'scope' => 'access',
-                                       'expires' => time() + ACCESS_TOKEN_DURATION,
-                                       'uid' => $uverifier
-                               ]
-                       );
-
-                       if ($r) {
-                               $ret = new OAuthToken($key, $sec);
-                       }
-               }
-
-               DBA::delete('tokens', ['id' => $token->key]);
-
-               if (!is_null($ret) && !is_null($uverifier)) {
-                       DI::config()->delete("oauth", $verifier);
-               }
-
-               return $ret;
-       }
-}
index f62b14c..6b31ad7 100644 (file)
@@ -25,7 +25,7 @@ use Friendica\Core\Logger;
 use Friendica\Core\Protocol;
 use Friendica\DI;
 use Friendica\Protocol\Activity;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
 
 /**
  * A list of threads
diff --git a/src/Security/ExAuth.php b/src/Security/ExAuth.php
new file mode 100644 (file)
index 0000000..87f236d
--- /dev/null
@@ -0,0 +1,391 @@
+<?php
+
+/**
+ * ejabberd extauth script for the integration with friendica
+ *
+ * Originally written for joomla by Dalibor Karlovic <dado@krizevci.info>
+ * modified for Friendica by Michael Vogel <icarus@dabo.de>
+ * published under GPL
+ *
+ * Latest version of the original script for joomla is available at:
+ * http://87.230.15.86/~dado/ejabberd/joomla-login
+ *
+ * Installation:
+ *
+ *     - Change it's owner to whichever user is running the server, ie. ejabberd
+ *       $ chown ejabberd:ejabberd /path/to/friendica/bin/auth_ejabberd.php
+ *
+ *     - Change the access mode so it is readable only to the user ejabberd and has exec
+ *       $ chmod 700 /path/to/friendica/bin/auth_ejabberd.php
+ *
+ *     - Edit your ejabberd.cfg file, comment out your auth_method and add:
+ *       {auth_method, external}.
+ *       {extauth_program, "/path/to/friendica/bin/auth_ejabberd.php"}.
+ *
+ *     - Restart your ejabberd service, you should be able to login with your friendica auth info
+ *
+ * Other hints:
+ *     - if your users have a space or a @ in their nickname, they'll run into trouble
+ *       registering with any client so they should be instructed to replace these chars
+ *       " " (space) is replaced with "%20"
+ *       "@" is replaced with "(a)"
+ *
+ */
+
+namespace Friendica\Security;
+
+use Exception;
+use Friendica\App;
+use Friendica\Core\Config\IConfig;
+use Friendica\Core\PConfig\IPConfig;
+use Friendica\Database\Database;
+use Friendica\DI;
+use Friendica\Model\User;
+use Friendica\Network\HTTPException;
+use Friendica\Util\PidFile;
+
+class ExAuth
+{
+       private $bDebug;
+       private $host;
+
+       /**
+        * @var App\Mode
+        */
+       private $appMode;
+       /**
+        * @var IConfig
+        */
+       private $config;
+       /**
+        * @var IPConfig
+        */
+       private $pConfig;
+       /**
+        * @var Database
+        */
+       private $dba;
+       /**
+        * @var App\BaseURL
+        */
+       private $baseURL;
+
+       /**
+        * @param App\Mode    $appMode
+        * @param IConfig      $config
+        * @param IPConfig     $pConfig
+        * @param Database    $dba
+        * @param App\BaseURL $baseURL
+        * @throws Exception
+        */
+       public function __construct(App\Mode $appMode, IConfig $config, IPConfig $pConfig, Database $dba, App\BaseURL $baseURL)
+       {
+               $this->appMode = $appMode;
+               $this->config  = $config;
+               $this->pConfig = $pConfig;
+               $this->dba     = $dba;
+               $this->baseURL = $baseURL;
+
+               $this->bDebug = (int)$config->get('jabber', 'debug');
+
+               openlog('auth_ejabberd', LOG_PID, LOG_USER);
+
+               $this->writeLog(LOG_NOTICE, 'start');
+       }
+
+       /**
+        * Standard input reading function, executes the auth with the provided
+        * parameters
+        *
+        * @throws HTTPException\InternalServerErrorException
+        */
+       public function readStdin()
+       {
+               if (!$this->appMode->isNormal()) {
+                       $this->writeLog(LOG_ERR, 'The node isn\'t ready.');
+                       return;
+               }
+
+               while (!feof(STDIN)) {
+                       // Quit if the database connection went down
+                       if (!$this->dba->isConnected()) {
+                               $this->writeLog(LOG_ERR, 'the database connection went down');
+                               return;
+                       }
+
+                       $iHeader = fgets(STDIN, 3);
+                       if (empty($iHeader)) {
+                               $this->writeLog(LOG_ERR, 'empty stdin');
+                               return;
+                       }
+
+                       $aLength = unpack('n', $iHeader);
+                       $iLength = $aLength['1'];
+
+                       // No data? Then quit
+                       if ($iLength == 0) {
+                               $this->writeLog(LOG_ERR, 'we got no data, quitting');
+                               return;
+                       }
+
+                       // Fetching the data
+                       $sData = fgets(STDIN, $iLength + 1);
+                       $this->writeLog(LOG_DEBUG, 'received data: ' . $sData);
+                       $aCommand = explode(':', $sData);
+                       if (is_array($aCommand)) {
+                               switch ($aCommand[0]) {
+                                       case 'isuser':
+                                               // Check the existance of a given username
+                                               $this->isUser($aCommand);
+                                               break;
+                                       case 'auth':
+                                               // Check if the givven password is correct
+                                               $this->auth($aCommand);
+                                               break;
+                                       case 'setpass':
+                                               // We don't accept the setting of passwords here
+                                               $this->writeLog(LOG_NOTICE, 'setpass command disabled');
+                                               fwrite(STDOUT, pack('nn', 2, 0));
+                                               break;
+                                       default:
+                                               // We don't know the given command
+                                               $this->writeLog(LOG_NOTICE, 'unknown command ' . $aCommand[0]);
+                                               fwrite(STDOUT, pack('nn', 2, 0));
+                                               break;
+                               }
+                       } else {
+                               $this->writeLog(LOG_NOTICE, 'invalid command string ' . $sData);
+                               fwrite(STDOUT, pack('nn', 2, 0));
+                       }
+               }
+       }
+
+       /**
+        * Check if the given username exists
+        *
+        * @param array $aCommand The command array
+        * @throws HTTPException\InternalServerErrorException
+        */
+       private function isUser(array $aCommand)
+       {
+               // Check if there is a username
+               if (!isset($aCommand[1])) {
+                       $this->writeLog(LOG_NOTICE, 'invalid isuser command, no username given');
+                       fwrite(STDOUT, pack('nn', 2, 0));
+                       return;
+               }
+
+               // We only allow one process per hostname. So we set a lock file
+               // Problem: We get the firstname after the first auth - not before
+               $this->setHost($aCommand[2]);
+
+               // Now we check if the given user is valid
+               $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]);
+
+               // Does the hostname match? So we try directly
+               if ($this->baseURL->getHostname() == $aCommand[2]) {
+                       $this->writeLog(LOG_INFO, 'internal user check for ' . $sUser . '@' . $aCommand[2]);
+                       $found = $this->dba->exists('user', ['nickname' => $sUser]);
+               } else {
+                       $found = false;
+               }
+
+               // If the hostnames doesn't match or there is some failure, we try to check remotely
+               if (!$found) {
+                       $found = $this->checkUser($aCommand[2], $aCommand[1], true);
+               }
+
+               if ($found) {
+                       // The user is okay
+                       $this->writeLog(LOG_NOTICE, 'valid user: ' . $sUser);
+                       fwrite(STDOUT, pack('nn', 2, 1));
+               } else {
+                       // The user isn't okay
+                       $this->writeLog(LOG_WARNING, 'invalid user: ' . $sUser);
+                       fwrite(STDOUT, pack('nn', 2, 0));
+               }
+       }
+
+       /**
+        * Check remote user existance via HTTP(S)
+        *
+        * @param string  $host The hostname
+        * @param string  $user Username
+        * @param boolean $ssl  Should the check be done via SSL?
+        *
+        * @return boolean Was the user found?
+        * @throws HTTPException\InternalServerErrorException
+        */
+       private function checkUser($host, $user, $ssl)
+       {
+               $this->writeLog(LOG_INFO, 'external user check for ' . $user . '@' . $host);
+
+               $url = ($ssl ? 'https' : 'http') . '://' . $host . '/noscrape/' . $user;
+
+               $curlResult = DI::httpRequest()->get($url);
+
+               if (!$curlResult->isSuccess()) {
+                       return false;
+               }
+
+               if ($curlResult->getReturnCode() != 200) {
+                       return false;
+               }
+
+               $json = @json_decode($curlResult->getBody());
+               if (!is_object($json)) {
+                       return false;
+               }
+
+               return $json->nick == $user;
+       }
+
+       /**
+        * Authenticate the given user and password
+        *
+        * @param array $aCommand The command array
+        * @throws Exception
+        */
+       private function auth(array $aCommand)
+       {
+               // check user authentication
+               if (sizeof($aCommand) != 4) {
+                       $this->writeLog(LOG_NOTICE, 'invalid auth command, data missing');
+                       fwrite(STDOUT, pack('nn', 2, 0));
+                       return;
+               }
+
+               // We only allow one process per hostname. So we set a lock file
+               // Problem: We get the firstname after the first auth - not before
+               $this->setHost($aCommand[2]);
+
+               // We now check if the password match
+               $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]);
+
+               $Error = false;
+               // Does the hostname match? So we try directly
+               if ($this->baseURL->getHostname() == $aCommand[2]) {
+                       try {
+                               $this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $aCommand[2]);
+                               User::getIdFromPasswordAuthentication($sUser, $aCommand[3], true);
+                       } catch (HTTPException\ForbiddenException $ex) {
+                               // User exists, authentication failed
+                               $this->writeLog(LOG_INFO, 'check against alternate password for ' . $sUser . '@' . $aCommand[2]);
+                               $aUser = User::getByNickname($sUser, ['uid']);
+                               $sPassword = $this->pConfig->get($aUser['uid'], 'xmpp', 'password', null, true);
+                               $Error = ($aCommand[3] != $sPassword);
+                       } catch (\Throwable $ex) {
+                               // User doesn't exist and any other failure case
+                               $this->writeLog(LOG_WARNING, $ex->getMessage() . ': ' . $sUser);
+                               $Error = true;
+                       }
+               } else {
+                       $Error = true;
+               }
+
+               // If the hostnames doesn't match or there is some failure, we try to check remotely
+               if ($Error && !$this->checkCredentials($aCommand[2], $aCommand[1], $aCommand[3], true)) {
+                       $this->writeLog(LOG_WARNING, 'authentification failed for user ' . $sUser . '@' . $aCommand[2]);
+                       fwrite(STDOUT, pack('nn', 2, 0));
+               } else {
+                       $this->writeLog(LOG_NOTICE, 'authentificated user ' . $sUser . '@' . $aCommand[2]);
+                       fwrite(STDOUT, pack('nn', 2, 1));
+               }
+       }
+
+       /**
+        * Check remote credentials via HTTP(S)
+        *
+        * @param string $host The hostname
+        * @param string $user Username
+        * @param string $password Password
+        * @param boolean $ssl Should the check be done via SSL?
+        *
+        * @return boolean Are the credentials okay?
+        */
+       private function checkCredentials($host, $user, $password, $ssl)
+       {
+               $this->writeLog(LOG_INFO, 'external credential check for ' . $user . '@' . $host);
+
+               $url = ($ssl ? 'https' : 'http') . '://' . $host . '/api/account/verify_credentials.json?skip_status=true';
+
+               $ch = curl_init();
+               curl_setopt($ch, CURLOPT_URL, $url);
+               curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+               curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
+               curl_setopt($ch, CURLOPT_HEADER, true);
+               curl_setopt($ch, CURLOPT_NOBODY, true);
+               curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+               curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $password);
+
+               curl_exec($ch);
+               $curl_info = @curl_getinfo($ch);
+               $http_code = $curl_info['http_code'];
+               curl_close($ch);
+
+               $this->writeLog(LOG_INFO, 'external auth for ' . $user . '@' . $host . ' returned ' . $http_code);
+
+               return $http_code == 200;
+       }
+
+       /**
+        * Set the hostname for this process
+        *
+        * @param string $host The hostname
+        */
+       private function setHost($host)
+       {
+               if (!empty($this->host)) {
+                       return;
+               }
+
+               $this->writeLog(LOG_INFO, 'Hostname for process ' . getmypid() . ' is ' . $host);
+
+               $this->host = $host;
+
+               $lockpath = $this->config->get('jabber', 'lockpath');
+               if (is_null($lockpath)) {
+                       $this->writeLog(LOG_INFO, 'No lockpath defined.');
+                       return;
+               }
+
+               $file = $lockpath . DIRECTORY_SEPARATOR . $host;
+               if (PidFile::isRunningProcess($file)) {
+                       if (PidFile::killProcess($file)) {
+                               $this->writeLog(LOG_INFO, 'Old process was successfully killed');
+                       } else {
+                               $this->writeLog(LOG_ERR, "The old Process wasn't killed in time. We now quit our process.");
+                               die();
+                       }
+               }
+
+               // Now it is safe to create the pid file
+               PidFile::create($file);
+               if (!file_exists($file)) {
+                       $this->writeLog(LOG_WARNING, 'Logfile ' . $file . " couldn't be created.");
+               }
+       }
+
+       /**
+        * write data to the syslog
+        *
+        * @param integer $loglevel The syslog loglevel
+        * @param string $sMessage The syslog message
+        */
+       private function writeLog($loglevel, $sMessage)
+       {
+               if (!$this->bDebug && ($loglevel >= LOG_DEBUG)) {
+                       return;
+               }
+               syslog($loglevel, $sMessage);
+       }
+
+       /**
+        * destroy the class, close the syslog connection.
+        */
+       public function __destruct()
+       {
+               $this->writeLog(LOG_NOTICE, 'stop');
+               closelog();
+       }
+}
diff --git a/src/Security/FKOAuth1.php b/src/Security/FKOAuth1.php
new file mode 100644 (file)
index 0000000..48f8a54
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Network;
+
+use Friendica\Core\Logger;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Security\FKOAuthDataStore;
+use OAuthServer;
+use OAuthSignatureMethod_HMAC_SHA1;
+use OAuthSignatureMethod_PLAINTEXT;
+
+/**
+ * OAuth protocol
+ */
+class FKOAuth1 extends OAuthServer
+{
+       /**
+        * Constructor
+        */
+       public function __construct()
+       {
+               parent::__construct(new FKOAuthDataStore());
+               $this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
+               $this->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());
+       }
+
+       /**
+        * @param string $uid user id
+        * @return void
+        * @throws HTTPException\ForbiddenException
+        * @throws HTTPException\InternalServerErrorException
+        */
+       public function loginUser($uid)
+       {
+               Logger::notice("FKOAuth1::loginUser $uid");
+               $a = DI::app();
+               $record = DBA::selectFirst('user', [], ['uid' => $uid, 'blocked' => 0, 'account_expired' => 0, 'account_removed' => 0, 'verified' => 1]);
+
+               if (!DBA::isResult($record)) {
+                       Logger::info('FKOAuth1::loginUser failure', ['server' => $_SERVER]);
+                       header('HTTP/1.0 401 Unauthorized');
+                       die('This api requires login');
+               }
+
+               DI::auth()->setForUser($a, $record, true);
+       }
+}
diff --git a/src/Security/FKOAuthDataStore.php b/src/Security/FKOAuthDataStore.php
new file mode 100644 (file)
index 0000000..d9c6895
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Security;
+
+use Friendica\Core\Logger;
+use Friendica\Database\DBA;
+use Friendica\DI;
+use Friendica\Util\Strings;
+use OAuthConsumer;
+use OAuthDataStore;
+use OAuthToken;
+
+define('REQUEST_TOKEN_DURATION', 300);
+define('ACCESS_TOKEN_DURATION', 31536000);
+
+/**
+ * OAuthDataStore class
+ */
+class FKOAuthDataStore extends OAuthDataStore
+{
+       /**
+        * @return string
+        * @throws \Exception
+        */
+       private static function genToken()
+       {
+               return Strings::getRandomHex(32);
+       }
+
+       /**
+        * @param string $consumer_key key
+        * @return OAuthConsumer|null
+        * @throws \Exception
+        */
+       public function lookup_consumer($consumer_key)
+       {
+               Logger::log(__function__ . ":" . $consumer_key);
+
+               $s = DBA::select('clients', ['client_id', 'pw', 'redirect_uri'], ['client_id' => $consumer_key]);
+               $r = DBA::toArray($s);
+
+               if (DBA::isResult($r)) {
+                       return new OAuthConsumer($r[0]['client_id'], $r[0]['pw'], $r[0]['redirect_uri']);
+               }
+
+               return null;
+       }
+
+       /**
+        * @param OAuthConsumer $consumer
+        * @param string        $token_type
+        * @param string        $token_id
+        * @return OAuthToken|null
+        * @throws \Exception
+        */
+       public function lookup_token(OAuthConsumer $consumer, $token_type, $token_id)
+       {
+               Logger::log(__function__ . ":" . $consumer . ", " . $token_type . ", " . $token_id);
+
+               $s = DBA::select('tokens', ['id', 'secret', 'scope', 'expires', 'uid'], ['client_id' => $consumer->key, 'scope' => $token_type, 'id' => $token_id]);
+               $r = DBA::toArray($s);
+
+               if (DBA::isResult($r)) {
+                       $ot = new OAuthToken($r[0]['id'], $r[0]['secret']);
+                       $ot->scope = $r[0]['scope'];
+                       $ot->expires = $r[0]['expires'];
+                       $ot->uid = $r[0]['uid'];
+                       return $ot;
+               }
+
+               return null;
+       }
+
+       /**
+        * @param OAuthConsumer $consumer
+        * @param OAuthToken    $token
+        * @param string        $nonce
+        * @param int           $timestamp
+        * @return mixed
+        * @throws \Exception
+        */
+       public function lookup_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
+       {
+               $token = DBA::selectFirst('tokens', ['id', 'secret'], ['client_id' => $consumer->key, 'id' => $nonce, 'expires' => $timestamp]);
+               if (DBA::isResult($token)) {
+                       return new OAuthToken($token['id'], $token['secret']);
+               }
+
+               return null;
+       }
+
+       /**
+        * @param OAuthConsumer $consumer
+        * @param string        $callback
+        * @return OAuthToken|null
+        * @throws \Exception
+        */
+       public function new_request_token(OAuthConsumer $consumer, $callback = null)
+       {
+               Logger::log(__function__ . ":" . $consumer . ", " . $callback);
+               $key = self::genToken();
+               $sec = self::genToken();
+
+               if ($consumer->key) {
+                       $k = $consumer->key;
+               } else {
+                       $k = $consumer;
+               }
+
+               $r = DBA::insert(
+                       'tokens',
+                       [
+                               'id' => $key,
+                               'secret' => $sec,
+                               'client_id' => $k,
+                               'scope' => 'request',
+                               'expires' => time() + REQUEST_TOKEN_DURATION
+                       ]
+               );
+
+               if (!$r) {
+                       return null;
+               }
+
+               return new OAuthToken($key, $sec);
+       }
+
+       /**
+        * @param OAuthToken    $token    token
+        * @param OAuthConsumer $consumer consumer
+        * @param string        $verifier optional, defult null
+        * @return OAuthToken
+        * @throws \Exception
+        */
+       public function new_access_token(OAuthToken $token, OAuthConsumer $consumer, $verifier = null)
+       {
+               Logger::log(__function__ . ":" . $token . ", " . $consumer . ", " . $verifier);
+
+               // return a new access token attached to this consumer
+               // for the user associated with this token if the request token
+               // is authorized
+               // should also invalidate the request token
+
+               $ret = null;
+
+               // get user for this verifier
+               $uverifier = DI::config()->get("oauth", $verifier);
+               Logger::log(__function__ . ":" . $verifier . "," . $uverifier);
+
+               if (is_null($verifier) || ($uverifier !== false)) {
+                       $key = self::genToken();
+                       $sec = self::genToken();
+                       $r = DBA::insert(
+                               'tokens',
+                               [
+                                       'id' => $key,
+                                       'secret' => $sec,
+                                       'client_id' => $consumer->key,
+                                       'scope' => 'access',
+                                       'expires' => time() + ACCESS_TOKEN_DURATION,
+                                       'uid' => $uverifier
+                               ]
+                       );
+
+                       if ($r) {
+                               $ret = new OAuthToken($key, $sec);
+                       }
+               }
+
+               DBA::delete('tokens', ['id' => $token->key]);
+
+               if (!is_null($ret) && !is_null($uverifier)) {
+                       DI::config()->delete("oauth", $verifier);
+               }
+
+               return $ret;
+       }
+}
diff --git a/src/Security/Security.php b/src/Security/Security.php
new file mode 100644 (file)
index 0000000..a75b916
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Security;
+
+use Friendica\Database\DBA;
+use Friendica\Model\Contact;
+use Friendica\Model\Group;
+use Friendica\Model\User;
+use Friendica\Core\Session;
+
+/**
+ * Secures that User is allow to do requests
+ */
+class Security
+{
+       public static function canWriteToUserWall($owner)
+       {
+               static $verified = 0;
+
+               if (!Session::isAuthenticated()) {
+                       return false;
+               }
+
+               $uid = local_user();
+               if ($uid == $owner) {
+                       return true;
+               }
+
+               if (local_user() && ($owner == 0)) {
+                       return true;
+               }
+
+               if (!empty(Session::getRemoteContactID($owner))) {
+                       // use remembered decision and avoid a DB lookup for each and every display item
+                       // DO NOT use this function if there are going to be multiple owners
+                       // We have a contact-id for an authenticated remote user, this block determines if the contact
+                       // belongs to this page owner, and has the necessary permissions to post content
+
+                       if ($verified === 2) {
+                               return true;
+                       } elseif ($verified === 1) {
+                               return false;
+                       } else {
+                               $cid = Session::getRemoteContactID($owner);
+                               if (!$cid) {
+                                       return false;
+                               }
+
+                               $r = q("SELECT `contact`.*, `user`.`page-flags` FROM `contact` INNER JOIN `user` on `user`.`uid` = `contact`.`uid`
+                                       WHERE `contact`.`uid` = %d AND `contact`.`id` = %d AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
+                                       AND `user`.`blockwall` = 0 AND `readonly` = 0  AND (`contact`.`rel` IN (%d , %d) OR `user`.`page-flags` = %d) LIMIT 1",
+                                       intval($owner),
+                                       intval($cid),
+                                       intval(Contact::SHARING),
+                                       intval(Contact::FRIEND),
+                                       intval(User::PAGE_FLAGS_COMMUNITY)
+                               );
+
+                               if (DBA::isResult($r)) {
+                                       $verified = 2;
+                                       return true;
+                               } else {
+                                       $verified = 1;
+                               }
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Create a permission string for an element based on the visitor
+        *
+        * @param integer $owner_id   User ID of the owner of the element
+        * @param boolean $accessible Should the element be accessible anyway?
+        * @return string SQL permissions
+        */
+       public static function getPermissionsSQLByUserId(int $owner_id, bool $accessible = false)
+       {
+               $local_user = local_user();
+               $remote_contact = Session::getRemoteContactID($owner_id);
+               $acc_sql = '';
+
+               if ($accessible) {
+                       $acc_sql = ' OR `accessible`';
+               }
+
+               /*
+                * Construct permissions
+                *
+                * default permissions - anonymous user
+                */
+               $sql = " AND (allow_cid = ''
+                        AND allow_gid = ''
+                        AND deny_cid  = ''
+                        AND deny_gid  = ''" . $acc_sql . ") ";
+
+               /*
+                * Profile owner - everything is visible
+                */
+               if ($local_user && $local_user == $owner_id) {
+                       $sql = '';
+               /*
+                * Authenticated visitor. Load the groups the visitor belongs to.
+                */
+               } elseif ($remote_contact) {
+                       $gs = '<<>>'; // should be impossible to match
+
+                       $groups = Group::getIdsByContactId($remote_contact);
+
+                       if (is_array($groups)) {
+                               foreach ($groups as $g) {
+                                       $gs .= '|<' . intval($g) . '>';
+                               }
+                       }
+
+                       $sql = sprintf(
+                               " AND (NOT (deny_cid REGEXP '<%d>' OR deny_gid REGEXP '%s')
+                                 AND (allow_cid REGEXP '<%d>' OR allow_gid REGEXP '%s'
+                                 OR (allow_cid = '' AND allow_gid = ''))" . $acc_sql . ") ",
+                               intval($remote_contact),
+                               DBA::escape($gs),
+                               intval($remote_contact),
+                               DBA::escape($gs)
+                       );
+               }
+               return $sql;
+       }
+}
diff --git a/src/Util/ExAuth.php b/src/Util/ExAuth.php
deleted file mode 100644 (file)
index 7771712..0000000
+++ /dev/null
@@ -1,390 +0,0 @@
-<?php
-
-/**
- * ejabberd extauth script for the integration with friendica
- *
- * Originally written for joomla by Dalibor Karlovic <dado@krizevci.info>
- * modified for Friendica by Michael Vogel <icarus@dabo.de>
- * published under GPL
- *
- * Latest version of the original script for joomla is available at:
- * http://87.230.15.86/~dado/ejabberd/joomla-login
- *
- * Installation:
- *
- *     - Change it's owner to whichever user is running the server, ie. ejabberd
- *       $ chown ejabberd:ejabberd /path/to/friendica/bin/auth_ejabberd.php
- *
- *     - Change the access mode so it is readable only to the user ejabberd and has exec
- *       $ chmod 700 /path/to/friendica/bin/auth_ejabberd.php
- *
- *     - Edit your ejabberd.cfg file, comment out your auth_method and add:
- *       {auth_method, external}.
- *       {extauth_program, "/path/to/friendica/bin/auth_ejabberd.php"}.
- *
- *     - Restart your ejabberd service, you should be able to login with your friendica auth info
- *
- * Other hints:
- *     - if your users have a space or a @ in their nickname, they'll run into trouble
- *       registering with any client so they should be instructed to replace these chars
- *       " " (space) is replaced with "%20"
- *       "@" is replaced with "(a)"
- *
- */
-
-namespace Friendica\Util;
-
-use Exception;
-use Friendica\App;
-use Friendica\Core\Config\IConfig;
-use Friendica\Core\PConfig\IPConfig;
-use Friendica\Database\Database;
-use Friendica\DI;
-use Friendica\Model\User;
-use Friendica\Network\HTTPException;
-
-class ExAuth
-{
-       private $bDebug;
-       private $host;
-
-       /**
-        * @var App\Mode
-        */
-       private $appMode;
-       /**
-        * @var IConfig
-        */
-       private $config;
-       /**
-        * @var IPConfig
-        */
-       private $pConfig;
-       /**
-        * @var Database
-        */
-       private $dba;
-       /**
-        * @var App\BaseURL
-        */
-       private $baseURL;
-
-       /**
-        * @param App\Mode    $appMode
-        * @param IConfig      $config
-        * @param IPConfig     $pConfig
-        * @param Database    $dba
-        * @param App\BaseURL $baseURL
-        * @throws Exception
-        */
-       public function __construct(App\Mode $appMode, IConfig $config, IPConfig $pConfig, Database $dba, App\BaseURL $baseURL)
-       {
-               $this->appMode = $appMode;
-               $this->config  = $config;
-               $this->pConfig = $pConfig;
-               $this->dba     = $dba;
-               $this->baseURL = $baseURL;
-
-               $this->bDebug = (int)$config->get('jabber', 'debug');
-
-               openlog('auth_ejabberd', LOG_PID, LOG_USER);
-
-               $this->writeLog(LOG_NOTICE, 'start');
-       }
-
-       /**
-        * Standard input reading function, executes the auth with the provided
-        * parameters
-        *
-        * @throws HTTPException\InternalServerErrorException
-        */
-       public function readStdin()
-       {
-               if (!$this->appMode->isNormal()) {
-                       $this->writeLog(LOG_ERR, 'The node isn\'t ready.');
-                       return;
-               }
-
-               while (!feof(STDIN)) {
-                       // Quit if the database connection went down
-                       if (!$this->dba->isConnected()) {
-                               $this->writeLog(LOG_ERR, 'the database connection went down');
-                               return;
-                       }
-
-                       $iHeader = fgets(STDIN, 3);
-                       if (empty($iHeader)) {
-                               $this->writeLog(LOG_ERR, 'empty stdin');
-                               return;
-                       }
-
-                       $aLength = unpack('n', $iHeader);
-                       $iLength = $aLength['1'];
-
-                       // No data? Then quit
-                       if ($iLength == 0) {
-                               $this->writeLog(LOG_ERR, 'we got no data, quitting');
-                               return;
-                       }
-
-                       // Fetching the data
-                       $sData = fgets(STDIN, $iLength + 1);
-                       $this->writeLog(LOG_DEBUG, 'received data: ' . $sData);
-                       $aCommand = explode(':', $sData);
-                       if (is_array($aCommand)) {
-                               switch ($aCommand[0]) {
-                                       case 'isuser':
-                                               // Check the existance of a given username
-                                               $this->isUser($aCommand);
-                                               break;
-                                       case 'auth':
-                                               // Check if the givven password is correct
-                                               $this->auth($aCommand);
-                                               break;
-                                       case 'setpass':
-                                               // We don't accept the setting of passwords here
-                                               $this->writeLog(LOG_NOTICE, 'setpass command disabled');
-                                               fwrite(STDOUT, pack('nn', 2, 0));
-                                               break;
-                                       default:
-                                               // We don't know the given command
-                                               $this->writeLog(LOG_NOTICE, 'unknown command ' . $aCommand[0]);
-                                               fwrite(STDOUT, pack('nn', 2, 0));
-                                               break;
-                               }
-                       } else {
-                               $this->writeLog(LOG_NOTICE, 'invalid command string ' . $sData);
-                               fwrite(STDOUT, pack('nn', 2, 0));
-                       }
-               }
-       }
-
-       /**
-        * Check if the given username exists
-        *
-        * @param array $aCommand The command array
-        * @throws HTTPException\InternalServerErrorException
-        */
-       private function isUser(array $aCommand)
-       {
-               // Check if there is a username
-               if (!isset($aCommand[1])) {
-                       $this->writeLog(LOG_NOTICE, 'invalid isuser command, no username given');
-                       fwrite(STDOUT, pack('nn', 2, 0));
-                       return;
-               }
-
-               // We only allow one process per hostname. So we set a lock file
-               // Problem: We get the firstname after the first auth - not before
-               $this->setHost($aCommand[2]);
-
-               // Now we check if the given user is valid
-               $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]);
-
-               // Does the hostname match? So we try directly
-               if ($this->baseURL->getHostname() == $aCommand[2]) {
-                       $this->writeLog(LOG_INFO, 'internal user check for ' . $sUser . '@' . $aCommand[2]);
-                       $found = $this->dba->exists('user', ['nickname' => $sUser]);
-               } else {
-                       $found = false;
-               }
-
-               // If the hostnames doesn't match or there is some failure, we try to check remotely
-               if (!$found) {
-                       $found = $this->checkUser($aCommand[2], $aCommand[1], true);
-               }
-
-               if ($found) {
-                       // The user is okay
-                       $this->writeLog(LOG_NOTICE, 'valid user: ' . $sUser);
-                       fwrite(STDOUT, pack('nn', 2, 1));
-               } else {
-                       // The user isn't okay
-                       $this->writeLog(LOG_WARNING, 'invalid user: ' . $sUser);
-                       fwrite(STDOUT, pack('nn', 2, 0));
-               }
-       }
-
-       /**
-        * Check remote user existance via HTTP(S)
-        *
-        * @param string  $host The hostname
-        * @param string  $user Username
-        * @param boolean $ssl  Should the check be done via SSL?
-        *
-        * @return boolean Was the user found?
-        * @throws HTTPException\InternalServerErrorException
-        */
-       private function checkUser($host, $user, $ssl)
-       {
-               $this->writeLog(LOG_INFO, 'external user check for ' . $user . '@' . $host);
-
-               $url = ($ssl ? 'https' : 'http') . '://' . $host . '/noscrape/' . $user;
-
-               $curlResult = DI::httpRequest()->get($url);
-
-               if (!$curlResult->isSuccess()) {
-                       return false;
-               }
-
-               if ($curlResult->getReturnCode() != 200) {
-                       return false;
-               }
-
-               $json = @json_decode($curlResult->getBody());
-               if (!is_object($json)) {
-                       return false;
-               }
-
-               return $json->nick == $user;
-       }
-
-       /**
-        * Authenticate the given user and password
-        *
-        * @param array $aCommand The command array
-        * @throws Exception
-        */
-       private function auth(array $aCommand)
-       {
-               // check user authentication
-               if (sizeof($aCommand) != 4) {
-                       $this->writeLog(LOG_NOTICE, 'invalid auth command, data missing');
-                       fwrite(STDOUT, pack('nn', 2, 0));
-                       return;
-               }
-
-               // We only allow one process per hostname. So we set a lock file
-               // Problem: We get the firstname after the first auth - not before
-               $this->setHost($aCommand[2]);
-
-               // We now check if the password match
-               $sUser = str_replace(['%20', '(a)'], [' ', '@'], $aCommand[1]);
-
-               $Error = false;
-               // Does the hostname match? So we try directly
-               if ($this->baseURL->getHostname() == $aCommand[2]) {
-                       try {
-                               $this->writeLog(LOG_INFO, 'internal auth for ' . $sUser . '@' . $aCommand[2]);
-                               User::getIdFromPasswordAuthentication($sUser, $aCommand[3], true);
-                       } catch (HTTPException\ForbiddenException $ex) {
-                               // User exists, authentication failed
-                               $this->writeLog(LOG_INFO, 'check against alternate password for ' . $sUser . '@' . $aCommand[2]);
-                               $aUser = User::getByNickname($sUser, ['uid']);
-                               $sPassword = $this->pConfig->get($aUser['uid'], 'xmpp', 'password', null, true);
-                               $Error = ($aCommand[3] != $sPassword);
-                       } catch (\Throwable $ex) {
-                               // User doesn't exist and any other failure case
-                               $this->writeLog(LOG_WARNING, $ex->getMessage() . ': ' . $sUser);
-                               $Error = true;
-                       }
-               } else {
-                       $Error = true;
-               }
-
-               // If the hostnames doesn't match or there is some failure, we try to check remotely
-               if ($Error && !$this->checkCredentials($aCommand[2], $aCommand[1], $aCommand[3], true)) {
-                       $this->writeLog(LOG_WARNING, 'authentification failed for user ' . $sUser . '@' . $aCommand[2]);
-                       fwrite(STDOUT, pack('nn', 2, 0));
-               } else {
-                       $this->writeLog(LOG_NOTICE, 'authentificated user ' . $sUser . '@' . $aCommand[2]);
-                       fwrite(STDOUT, pack('nn', 2, 1));
-               }
-       }
-
-       /**
-        * Check remote credentials via HTTP(S)
-        *
-        * @param string $host The hostname
-        * @param string $user Username
-        * @param string $password Password
-        * @param boolean $ssl Should the check be done via SSL?
-        *
-        * @return boolean Are the credentials okay?
-        */
-       private function checkCredentials($host, $user, $password, $ssl)
-       {
-               $this->writeLog(LOG_INFO, 'external credential check for ' . $user . '@' . $host);
-
-               $url = ($ssl ? 'https' : 'http') . '://' . $host . '/api/account/verify_credentials.json?skip_status=true';
-
-               $ch = curl_init();
-               curl_setopt($ch, CURLOPT_URL, $url);
-               curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
-               curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
-               curl_setopt($ch, CURLOPT_HEADER, true);
-               curl_setopt($ch, CURLOPT_NOBODY, true);
-               curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
-               curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $password);
-
-               curl_exec($ch);
-               $curl_info = @curl_getinfo($ch);
-               $http_code = $curl_info['http_code'];
-               curl_close($ch);
-
-               $this->writeLog(LOG_INFO, 'external auth for ' . $user . '@' . $host . ' returned ' . $http_code);
-
-               return $http_code == 200;
-       }
-
-       /**
-        * Set the hostname for this process
-        *
-        * @param string $host The hostname
-        */
-       private function setHost($host)
-       {
-               if (!empty($this->host)) {
-                       return;
-               }
-
-               $this->writeLog(LOG_INFO, 'Hostname for process ' . getmypid() . ' is ' . $host);
-
-               $this->host = $host;
-
-               $lockpath = $this->config->get('jabber', 'lockpath');
-               if (is_null($lockpath)) {
-                       $this->writeLog(LOG_INFO, 'No lockpath defined.');
-                       return;
-               }
-
-               $file = $lockpath . DIRECTORY_SEPARATOR . $host;
-               if (PidFile::isRunningProcess($file)) {
-                       if (PidFile::killProcess($file)) {
-                               $this->writeLog(LOG_INFO, 'Old process was successfully killed');
-                       } else {
-                               $this->writeLog(LOG_ERR, "The old Process wasn't killed in time. We now quit our process.");
-                               die();
-                       }
-               }
-
-               // Now it is safe to create the pid file
-               PidFile::create($file);
-               if (!file_exists($file)) {
-                       $this->writeLog(LOG_WARNING, 'Logfile ' . $file . " couldn't be created.");
-               }
-       }
-
-       /**
-        * write data to the syslog
-        *
-        * @param integer $loglevel The syslog loglevel
-        * @param string $sMessage The syslog message
-        */
-       private function writeLog($loglevel, $sMessage)
-       {
-               if (!$this->bDebug && ($loglevel >= LOG_DEBUG)) {
-                       return;
-               }
-               syslog($loglevel, $sMessage);
-       }
-
-       /**
-        * destroy the class, close the syslog connection.
-        */
-       public function __destruct()
-       {
-               $this->writeLog(LOG_NOTICE, 'stop');
-               closelog();
-       }
-}
diff --git a/src/Util/Security.php b/src/Util/Security.php
deleted file mode 100644 (file)
index 4233382..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Util;
-
-use Friendica\Database\DBA;
-use Friendica\Model\Contact;
-use Friendica\Model\Group;
-use Friendica\Model\User;
-use Friendica\Core\Session;
-
-/**
- * Secures that User is allow to do requests
- */
-class Security
-{
-       public static function canWriteToUserWall($owner)
-       {
-               static $verified = 0;
-
-               if (!Session::isAuthenticated()) {
-                       return false;
-               }
-
-               $uid = local_user();
-               if ($uid == $owner) {
-                       return true;
-               }
-
-               if (local_user() && ($owner == 0)) {
-                       return true;
-               }
-
-               if (!empty(Session::getRemoteContactID($owner))) {
-                       // use remembered decision and avoid a DB lookup for each and every display item
-                       // DO NOT use this function if there are going to be multiple owners
-                       // We have a contact-id for an authenticated remote user, this block determines if the contact
-                       // belongs to this page owner, and has the necessary permissions to post content
-
-                       if ($verified === 2) {
-                               return true;
-                       } elseif ($verified === 1) {
-                               return false;
-                       } else {
-                               $cid = Session::getRemoteContactID($owner);
-                               if (!$cid) {
-                                       return false;
-                               }
-
-                               $r = q("SELECT `contact`.*, `user`.`page-flags` FROM `contact` INNER JOIN `user` on `user`.`uid` = `contact`.`uid`
-                                       WHERE `contact`.`uid` = %d AND `contact`.`id` = %d AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
-                                       AND `user`.`blockwall` = 0 AND `readonly` = 0  AND (`contact`.`rel` IN (%d , %d) OR `user`.`page-flags` = %d) LIMIT 1",
-                                       intval($owner),
-                                       intval($cid),
-                                       intval(Contact::SHARING),
-                                       intval(Contact::FRIEND),
-                                       intval(User::PAGE_FLAGS_COMMUNITY)
-                               );
-
-                               if (DBA::isResult($r)) {
-                                       $verified = 2;
-                                       return true;
-                               } else {
-                                       $verified = 1;
-                               }
-                       }
-               }
-
-               return false;
-       }
-
-       /**
-        * Create a permission string for an element based on the visitor
-        *
-        * @param integer $owner_id   User ID of the owner of the element
-        * @param boolean $accessible Should the element be accessible anyway?
-        * @return string SQL permissions
-        */
-       public static function getPermissionsSQLByUserId(int $owner_id, bool $accessible = false)
-       {
-               $local_user = local_user();
-               $remote_contact = Session::getRemoteContactID($owner_id);
-               $acc_sql = '';
-
-               if ($accessible) {
-                       $acc_sql = ' OR `accessible`';
-               }
-
-               /*
-                * Construct permissions
-                *
-                * default permissions - anonymous user
-                */
-               $sql = " AND (allow_cid = ''
-                        AND allow_gid = ''
-                        AND deny_cid  = ''
-                        AND deny_gid  = ''" . $acc_sql . ") ";
-
-               /*
-                * Profile owner - everything is visible
-                */
-               if ($local_user && $local_user == $owner_id) {
-                       $sql = '';
-               /*
-                * Authenticated visitor. Load the groups the visitor belongs to.
-                */
-               } elseif ($remote_contact) {
-                       $gs = '<<>>'; // should be impossible to match
-
-                       $groups = Group::getIdsByContactId($remote_contact);
-
-                       if (is_array($groups)) {
-                               foreach ($groups as $g) {
-                                       $gs .= '|<' . intval($g) . '>';
-                               }
-                       }
-
-                       $sql = sprintf(
-                               " AND (NOT (deny_cid REGEXP '<%d>' OR deny_gid REGEXP '%s')
-                                 AND (allow_cid REGEXP '<%d>' OR allow_gid REGEXP '%s'
-                                 OR (allow_cid = '' AND allow_gid = ''))" . $acc_sql . ") ",
-                               intval($remote_contact),
-                               DBA::escape($gs),
-                               intval($remote_contact),
-                               DBA::escape($gs)
-                       );
-               }
-               return $sql;
-       }
-}