use Dice\Dice;
use Friendica\App\Mode;
-use Friendica\Util\ExAuth;
+use Friendica\Security\ExAuth;
use Psr\Log\LoggerInterface;
if (sizeof($_SERVER["argv"]) == 0) {
/* Generic exception class
*/
-use Friendica\Network\FKOAuthDataStore;
+use Friendica\Security\FKOAuthDataStore;
if (!class_exists('OAuthException', false)) {
class OAuthException extends Exception
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;
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;
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)
{
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
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";
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;
+++ /dev/null
-<?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);
- }
-}
+++ /dev/null
-<?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;
- }
-}
use Friendica\Core\Protocol;
use Friendica\DI;
use Friendica\Protocol\Activity;
-use Friendica\Util\Security;
+use Friendica\Security\Security;
/**
* A list of threads
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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);
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
+++ /dev/null
-<?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();
- }
-}
+++ /dev/null
-<?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;
- }
-}