require dirname(__DIR__) . '/vendor/autoload.php';
-$a = Factory\DependencyFactory::setUp('auth_ejabbered', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('auth_ejabbered', $dice);
if ($a->getMode()->isNormal()) {
$oAuth = new ExAuth();
use Friendica\Factory;
-$a = Factory\DependencyFactory::setUp('console', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('console', $dice);
\Friendica\BaseObject::setApp($a);
(new Friendica\Core\Console($argv))->execute();
require dirname(__DIR__) . '/vendor/autoload.php';
-$a = Factory\DependencyFactory::setUp('daemon', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('daemon', $dice);
if ($a->getMode()->isInstall()) {
die("Friendica isn't properly installed yet.\n");
require dirname(__DIR__) . '/vendor/autoload.php';
-$a = Factory\DependencyFactory::setUp('worker', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('worker', $dice);
// Check the database structure and possibly fixes it
Update::check($a->getBasePath(), true, $a->getMode());
"npm-asset/fullcalendar": "^3.0.1",
"npm-asset/cropperjs": "1.2.2",
"npm-asset/imagesloaded": "4.1.4",
- "pear/console_table": "^1.3"
+ "pear/console_table": "^1.3",
+ "level-2/dice": ">1.0"
},
"repositories": [
{
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "eb985236d64ed0b0fe1fc2e4ac6616e2",
+ "content-hash": "b9ea7162aa7ede630a2090c883e1174b",
"packages": [
{
"name": "asika/simple-console",
],
"time": "2018-09-18T12:18:08+00:00"
},
+ {
+ "name": "level-2/dice",
+ "version": "4.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Level-2/Dice.git",
+ "reference": "e631f110f0520294fec902814c61cac26566023c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Level-2/Dice/zipball/e631f110f0520294fec902814c61cac26566023c",
+ "reference": "e631f110f0520294fec902814c61cac26566023c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.0.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Dice\\": "./"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Tom Butler",
+ "email": "tom@r.je"
+ }
+ ],
+ "description": "A minimalist Dependency injection container (DIC) for PHP. Please note: 3.0+ is only compatible with PHP 7.0. The 2.0 branch is compatbile with PHP 5.6.",
+ "homepage": "http://r.je/dice.html",
+ "keywords": [
+ "dependency injection",
+ "dependency injection container",
+ "di",
+ "ioc"
+ ],
+ "time": "2019-05-01T12:55:36+00:00"
+ },
{
"name": "lightopenid/lightopenid",
"version": "dev-master",
When two-factor authentication is enabled, you can show your recovery codes, including the ones you've already used.
You can freely regenerate a new set of fresh recovery codes, just be sure to replace the previous ones where you saved them as they won't be active anymore.
+
+## Third-party applications and API
+
+Third-party applications using the Friendica API can't accept two-factor time-based authentication codes.
+Instead, if you enabled two-factor authentication, you have to generate app-specific randomly generated long passwords to use in your apps instead of your regular account password.
+
+**Note**: Your regular password won't work at all when prompted in third-party apps if you enabled two-factor authentication.
+
+You can generate as many app-specific passwords as you want, they will be shown once to you just after you generated it.
+Just copy and paste it in your third-party app in the Friendica account password input field at this point.
+We recommend generating a single app-specific password for each separate third-party app you are using, using a meaningul description of the target app (like "Frienqa on my Fairphone 2").
+
+You can also revoke any and all app-specific password you generated this way.
+This may log you out of the third-party application(s) you used the revoked app-specific password to log in with.
\ No newline at end of file
if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) {
$record = $addon_auth['user_record'];
} else {
- $user_id = User::authenticate(trim($user), trim($password));
+ $user_id = User::authenticate(trim($user), trim($password), true);
if ($user_id !== false) {
$record = DBA::selectFirst('user', [], ['uid' => $user_id]);
}
require __DIR__ . '/vendor/autoload.php';
-$a = Factory\DependencyFactory::setUp('index', __DIR__, false);
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/static/dependencies.config.php');
-$a->runFrontend();
+$a = Factory\DependencyFactory::setUp('index', $dice, false);
+$a->runFrontend();
$this->getMode()->determine($this->getBasePath());
if ($this->getMode()->has(App\Mode::DBAVAILABLE)) {
- $this->profiler->update(
- $this->config->get('system', 'profiler', false),
- $this->config->get('rendertime', 'callstack', false));
+ $this->profiler->update($this->config);
Core\Hook::loadHooks();
- $loader = new ConfigFileLoader($this->getBasePath(), $this->mode);
+ $loader = new ConfigFileLoader($this->getBasePath());
Core\Hook::callAll('load_config', $loader);
}
namespace Friendica\App;
-use Friendica\Core\Config;
-use Friendica\Database\DBA;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Database\Database;
+use Friendica\Util\BasePath;
/**
* Mode of the current Friendica Node
*/
class Mode
{
- const LOCALCONFIGPRESENT = 1;
- const DBAVAILABLE = 2;
- const DBCONFIGAVAILABLE = 4;
+ const LOCALCONFIGPRESENT = 1;
+ const DBAVAILABLE = 2;
+ const DBCONFIGAVAILABLE = 4;
const MAINTENANCEDISABLED = 8;
/***
*/
private $basepath;
- public function __construct($basepath = '')
+ /**
+ * @var Database
+ */
+ private $database;
+
+ /**
+ * @var ConfigCache
+ */
+ private $configCache;
+
+ public function __construct(BasePath $basepath, Database $database, ConfigCache $configCache)
{
- $this->basepath = $basepath;
- $this->mode = 0;
+ $this->basepath = $basepath->getPath();
+ $this->database = $database;
+ $this->configCache = $configCache;
+ $this->mode = 0;
}
/**
* - App::MODE_MAINTENANCE: The maintenance mode has been set
* - App::MODE_NORMAL : Normal run with all features enabled
*
- * @param string $basepath the Basepath of the Application
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @param string $basePath the Basepath of the Application
+ *
+ * @return Mode returns itself
+ *
+ * @throws \Exception
*/
- public function determine($basepath = null)
+ public function determine($basePath = null)
{
- if (!empty($basepath)) {
- $this->basepath = $basepath;
+ if (!empty($basePath)) {
+ $this->basepath = $basePath;
}
$this->mode = 0;
if (!file_exists($this->basepath . '/config/local.config.php')
- && !file_exists($this->basepath . '/config/local.ini.php')
- && !file_exists($this->basepath . '/.htconfig.php')) {
- return;
+ && !file_exists($this->basepath . '/config/local.ini.php')
+ && !file_exists($this->basepath . '/.htconfig.php')) {
+ return $this;
}
$this->mode |= Mode::LOCALCONFIGPRESENT;
- if (!DBA::connected()) {
- return;
+ if (!$this->database->connected()) {
+ return $this;
}
$this->mode |= Mode::DBAVAILABLE;
- if (DBA::fetchFirst("SHOW TABLES LIKE 'config'") === false) {
- return;
+ if ($this->database->fetchFirst("SHOW TABLES LIKE 'config'") === false) {
+ return $this;
}
$this->mode |= Mode::DBCONFIGAVAILABLE;
- if (Config::get('system', 'maintenance')) {
- return;
+ if (!empty($this->configCache->get('system', 'maintenance')) ||
+ // Don't use Config or Configuration here because we're possibly BEFORE initializing the Configuration,
+ // so this could lead to a dependency circle
+ !empty($this->database->selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])['v'])) {
+ return $this;
}
$this->mode |= Mode::MAINTENANCEDISABLED;
+
+ return $this;
}
/**
public function isInstall()
{
return !$this->has(Mode::LOCALCONFIGPRESENT) ||
- !$this->has(MODE::DBCONFIGAVAILABLE);
+ !$this->has(MODE::DBCONFIGAVAILABLE);
}
/**
public function isNormal()
{
return $this->has(Mode::LOCALCONFIGPRESENT) &&
- $this->has(Mode::DBAVAILABLE) &&
- $this->has(Mode::DBCONFIGAVAILABLE) &&
- $this->has(Mode::MAINTENANCEDISABLED);
+ $this->has(Mode::DBAVAILABLE) &&
+ $this->has(Mode::DBCONFIGAVAILABLE) &&
+ $this->has(Mode::MAINTENANCEDISABLED);
}
}
$collector->addGroup('/2fa', function (RouteCollector $collector) {
$collector->addRoute(['GET', 'POST'], '[/]' , Module\Settings\TwoFactor\Index::class);
$collector->addRoute(['GET', 'POST'], '/recovery' , Module\Settings\TwoFactor\Recovery::class);
+ $collector->addRoute(['GET', 'POST'], '/app_specific' , Module\Settings\TwoFactor\AppSpecific::class);
$collector->addRoute(['GET', 'POST'], '/verify' , Module\Settings\TwoFactor\Verify::class);
});
});
$this->routeCollector->addRoute(['GET'], '/xrd', Module\Xrd::class);
}
- public function __construct(RouteCollector $routeCollector = null)
+ public function __construct()
{
- if (!$routeCollector) {
- $routeCollector = new RouteCollector(new Std(), new GroupCountBased());
- }
-
- $this->routeCollector = $routeCollector;
+ $this->routeCollector = new RouteCollector(new Std(), new GroupCountBased());
}
public function getRouteCollector()
$installer = new Installer();
$configCache = $a->getConfigCache();
- $installer->setUpCache($configCache, BasePath::create($a->getBasePath(), $_SERVER));
+ $basepath = new BasePath($a->getBasePath());
+ $installer->setUpCache($configCache, $basepath->getPath());
$this->out(" Complete!\n\n");
// We only call this so that a previously unknown contact can be added.
// This is important for the function "Model\Contact::getDetailsByURL()".
// This function then can fetch an entry from the contact table.
- Contact::getIdForURL($attributes['profile'], 0, true);
+ $default['url'] = $attributes['profile'];
+
+ if (!empty($attributes['author'])) {
+ $default['name'] = $attributes['author'];
+ }
+
+ if (!empty($attributes['avatar'])) {
+ $default['photo'] = $attributes['avatar'];
+ }
+
+ Contact::getIdForURL($attributes['profile'], 0, true, $default);
$author_contact = Contact::getDetailsByURL($attributes['profile']);
$author_contact['addr'] = defaults($author_contact, 'addr' , Protocol::getAddrFromProfileUrl($attributes['profile']));
use DOMDocument;
use Exception;
use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Database\Database;
use Friendica\Database\DBStructure;
-use Friendica\Factory\DBFactory;
use Friendica\Object\Image;
use Friendica\Util\Logger\VoidLogger;
use Friendica\Util\Network;
*/
public function checkDB(ConfigCache $configCache, Profiler $profiler)
{
- $database = DBFactory::init($configCache, $profiler, [], new VoidLogger());
+ $database = new Database($configCache, $profiler, new VoidLogger());
if ($database->connected()) {
if (DBStructure::existsTable('user')) {
namespace Friendica\Core\L10n;
+use Friendica\Core\Config\Configuration;
use Friendica\Core\Hook;
use Friendica\Core\Session;
use Friendica\Database\Database;
*/
private $logger;
- public function __construct(string $lang, Database $dba, LoggerInterface $logger)
+ public function __construct(Configuration $config, Database $dba, LoggerInterface $logger)
{
$this->dba = $dba;
$this->logger = $logger;
- $this->loadTranslationTable($lang);
+ $this->loadTranslationTable(L10n::detectLanguage($config->get('system', 'language', 'en')));
\Friendica\Core\L10n::init($this);
}
use mysqli;
use mysqli_result;
use mysqli_stmt;
-use ParagonIE\HiddenString\HiddenString;
use PDO;
use PDOException;
use PDOStatement;
private $in_transaction = false;
private $in_retrial = false;
private $relation = [];
- private $db_serveraddr;
- private $db_user;
- /**
- * @var HiddenString
- */
- private $db_pass;
- private $db_name;
- private $db_charset;
- public function __construct(ConfigCache $configCache, Profiler $profiler, LoggerInterface $logger, $serveraddr, $user, HiddenString $pass, $db, $charset = null)
+ public function __construct(ConfigCache $configCache, Profiler $profiler, LoggerInterface $logger, array $server = [])
{
// We are storing these values for being able to perform a reconnect
$this->configCache = $configCache;
$this->profiler = $profiler;
$this->logger = $logger;
- $this->db_serveraddr = $serveraddr;
- $this->db_user = $user;
- $this->db_pass = $pass;
- $this->db_name = $db;
- $this->db_charset = $charset;
+ $this->readServerVariables($server);
$this->connect();
DBA::init($this);
+
+ if ($this->isConnected()) {
+ // Loads DB_UPDATE_VERSION constant
+ DBStructure::definition($configCache->get('system', 'basepath'), false);
+ }
+ }
+
+ private function readServerVariables(array $server)
+ {
+ // Use environment variables for mysql if they are set beforehand
+ if (!empty($server['MYSQL_HOST'])
+ && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER']))
+ && $server['MYSQL_PASSWORD'] !== false
+ && !empty($server['MYSQL_DATABASE']))
+ {
+ $db_host = $server['MYSQL_HOST'];
+ if (!empty($server['MYSQL_PORT'])) {
+ $db_host .= ':' . $server['MYSQL_PORT'];
+ }
+ $this->configCache->set('database', 'hostname', $db_host);
+ unset($db_host);
+ if (!empty($server['MYSQL_USERNAME'])) {
+ $this->configCache->set('database', 'username', $server['MYSQL_USERNAME']);
+ } else {
+ $this->configCache->set('database', 'username', $server['MYSQL_USER']);
+ }
+ $this->configCache->set('database', 'password', (string) $server['MYSQL_PASSWORD']);
+ $this->configCache->set('database', 'database', $server['MYSQL_DATABASE']);
+ }
}
public function connect()
}
$port = 0;
- $serveraddr = trim($this->db_serveraddr);
-
+ $serveraddr = trim($this->configCache->get('database', 'hostname'));
$serverdata = explode(':', $serveraddr);
$server = $serverdata[0];
-
if (count($serverdata) > 1) {
$port = trim($serverdata[1]);
}
-
$server = trim($server);
- $user = trim($this->db_user);
- $pass = trim($this->db_pass);
- $db = trim($this->db_name);
- $charset = trim($this->db_charset);
+ $user = trim($this->configCache->get('database', 'username'));
+ $pass = trim($this->configCache->get('database', 'password'));
+ $db = trim($this->configCache->get('database', 'database'));
+ $charset = trim($this->configCache->get('database', 'charset'));
if (!(strlen($server) && strlen($user))) {
return false;
use Friendica\Core;
use Friendica\Core\Config;
use Friendica\Core\Config\Cache;
-use Friendica\Util\ConfigFileLoader;
use Friendica\Model\Config\Config as ConfigModel;
use Friendica\Model\Config\PConfig as PConfigModel;
+use Friendica\Util\ConfigFileLoader;
class ConfigFactory
{
*
* @return Cache\ConfigCache
*/
- public static function createCache(ConfigFileLoader $loader)
+ public function createCache(ConfigFileLoader $loader)
{
$configCache = new Cache\ConfigCache();
$loader->setupCache($configCache);
/**
* @param Cache\ConfigCache $configCache The config cache of this adapter
- * @param ConfigModel $configModel The configuration model
+ * @param ConfigModel $configModel The configuration model
*
* @return Config\Configuration
*/
- public static function createConfig(Cache\ConfigCache $configCache, ConfigModel $configModel)
+ public function createConfig(Cache\ConfigCache $configCache, ConfigModel $configModel)
{
if ($configCache->get('system', 'config_adapter') === 'preload') {
$configuration = new Config\PreloadConfiguration($configCache, $configModel);
}
/**
- * @param Cache\ConfigCache $configCache The config cache
- * @param Cache\PConfigCache $pConfigCache The personal config cache
- * @param PConfigModel $configModel The configuration model
+ * @param Cache\ConfigCache $configCache The config cache
+ * @param Cache\PConfigCache $pConfigCache The personal config cache
+ * @param PConfigModel $configModel The configuration model
*
* @return Config\PConfiguration
*/
- public static function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, PConfigModel $configModel)
+ public function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, PConfigModel $configModel)
{
if ($configCache->get('system', 'config_adapter') === 'preload') {
$configuration = new Config\PreloadPConfiguration($pConfigCache, $configModel);
+++ /dev/null
-<?php
-
-namespace Friendica\Factory;
-
-use Friendica\Core\Config\Cache;
-use Friendica\Database;
-use Friendica\Util\Logger\VoidLogger;
-use Friendica\Util\Profiler;
-use ParagonIE\HiddenString\HiddenString;
-
-class DBFactory
-{
- /**
- * Initialize the DBA connection
- *
- * @param Cache\ConfigCache $configCache The configuration cache
- * @param Profiler $profiler The profiler
- * @param array $server The $_SERVER variables
- *
- * @return Database\Database
- * @throws \Exception if connection went bad
- */
- public static function init(Cache\ConfigCache $configCache, Profiler $profiler, array $server)
- {
- $db_host = $configCache->get('database', 'hostname');
- $db_user = $configCache->get('database', 'username');
- $db_pass = $configCache->get('database', 'password');
- $db_data = $configCache->get('database', 'database');
- $charset = $configCache->get('database', 'charset');
-
- // Use environment variables for mysql if they are set beforehand
- if (!empty($server['MYSQL_HOST'])
- && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER']))
- && $server['MYSQL_PASSWORD'] !== false
- && !empty($server['MYSQL_DATABASE']))
- {
- $db_host = $server['MYSQL_HOST'];
- if (!empty($server['MYSQL_PORT'])) {
- $db_host .= ':' . $server['MYSQL_PORT'];
- }
- if (!empty($server['MYSQL_USERNAME'])) {
- $db_user = $server['MYSQL_USERNAME'];
- } else {
- $db_user = $server['MYSQL_USER'];
- }
- $db_pass = new HiddenString((string) $server['MYSQL_PASSWORD']);
- $db_data = $server['MYSQL_DATABASE'];
- }
-
- $database = new Database\Database($configCache, $profiler, new VoidLogger(), $db_host, $db_user, $db_pass, $db_data, $charset);
-
- if ($database->connected()) {
- // Loads DB_UPDATE_VERSION constant
- Database\DBStructure::definition($configCache->get('system', 'basepath'), false);
- }
-
- unset($db_host, $db_user, $db_pass, $db_data, $charset);
-
- return $database;
- }
-}
namespace Friendica\Factory;
+use Dice\Dice;
use Friendica\App;
-use Friendica\Core\Config\Cache\PConfigCache;
-use Friendica\Core\L10n\L10n;
-use Friendica\Factory;
-use Friendica\Util\BasePath;
-use Friendica\Util\BaseURL;
-use Friendica\Util\ConfigFileLoader;
+use Friendica\Core\Config\PConfiguration;
+use Psr\Log\LoggerInterface;
class DependencyFactory
{
* Setting all default-dependencies of a friendica execution
*
* @param string $channel The channel of this execution
- * @param string $directory The base directory
* @param bool $isBackend True, if it's a backend execution, otherwise false (Default true)
*
* @return App The application
*
* @throws \Exception
*/
- public static function setUp($channel, $directory, $isBackend = true)
+ public static function setUp($channel, Dice $dice, $isBackend = true)
{
- $basePath = BasePath::create($directory, $_SERVER);
- $mode = new App\Mode($basePath);
- $router = new App\Router();
- $configLoader = new ConfigFileLoader($basePath, $mode);
- $configCache = Factory\ConfigFactory::createCache($configLoader);
- $profiler = Factory\ProfilerFactory::create($configCache);
- $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER);
- $configModel = new \Friendica\Model\Config\Config($database);
- $config = Factory\ConfigFactory::createConfig($configCache, $configModel);
- // needed to call PConfig::init()
- $pconfigModel = new \Friendica\Model\Config\PConfig($database);
- Factory\ConfigFactory::createPConfig($configCache, new PConfigCache(), $pconfigModel);
- $logger = Factory\LoggerFactory::create($channel, $database, $config, $profiler);
- Factory\LoggerFactory::createDev($channel, $config, $profiler);
- $baseURL = new BaseURL($config, $_SERVER);
- $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
- $database,
- $logger);
+ $pConfig = $dice->create(PConfiguration::class);
+ $logger = $dice->create(LoggerInterface::class, [$channel]);
+ $devLogger = $dice->create('$devLogger', [$channel]);
- return new App($database, $config, $mode, $router, $baseURL, $logger, $profiler, $l10n, $isBackend);
+ return $dice->create(App::class, [$isBackend]);
}
}
* @throws \Exception
* @throws InternalServerErrorException
*/
- public static function create($channel, Database $database, Configuration $config, Profiler $profiler)
+ public function create($channel, Database $database, Configuration $config, Profiler $profiler)
{
if (empty($config->get('system', 'debugging', false))) {
$logger = new VoidLogger();
+++ /dev/null
-<?php
-
-namespace Friendica\Factory;
-
-use Friendica\Core\Config\Cache\ConfigCache;
-use Friendica\Util\Profiler;
-
-class ProfilerFactory
-{
- /**
- * Creates a Profiler for the current execution
- *
- * @param ConfigCache $configCache The configuration cache
- *
- * @return Profiler
- */
- public static function create(ConfigCache $configCache)
- {
- $enabled = $configCache->get('system', 'profiler');
- $enabled = isset($enabled) && $enabled !== '0';
- $renderTime = $configCache->get('rendertime', 'callstack');
- $renderTime = isset($renderTime) && $renderTime !== '0';
-
- return new Profiler($enabled, $renderTime);
- }
-}
// When we don't want to update, we look if we know this contact in any way
$data = self::getProbeDataFromDatabase($url, $contact_id);
$background_update = true;
- } elseif ($no_update && !empty($default)) {
+ } elseif ($no_update && !empty($default['network'])) {
// If there are default values, take these
$data = $default;
$background_update = false;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBA;
+use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings;
use Friendica\Util\XML;
use Friendica\Worker\Delivery;
-use Friendica\Protocol\ActivityPub;
use Text_LanguageDetect;
class Item extends BaseObject
return $item_id;
}
- ActivityPub\Processor::fetchMissingActivity($uri);
-
- /// @todo add Diaspora as well
+ if (ActivityPub\Processor::fetchMissingActivity($uri)) {
+ $item_id = self::searchByLink($uri, $uid);
+ } else {
+ $item_id = Diaspora::fetchByURL($uri);
+ }
- $item_id = self::searchByLink($uri, $uid);
if (!empty($item_id)) {
return $item_id;
}
--- /dev/null
+<?php
+
+namespace Friendica\Model\TwoFactor;
+
+use Friendica\BaseObject;
+use Friendica\Database\DBA;
+use Friendica\Model\User;
+use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Temporal;
+use PragmaRX\Random\Random;
+
+/**
+ * Manages users' two-factor recovery hashed_passwords in the 2fa_app_specific_passwords table
+ *
+ * @package Friendica\Model
+ */
+class AppSpecificPassword extends BaseObject
+{
+ public static function countForUser($uid)
+ {
+ return DBA::count('2fa_app_specific_password', ['uid' => $uid]);
+ }
+
+ public static function checkDuplicateForUser($uid, $description)
+ {
+ return DBA::exists('2fa_app_specific_password', ['uid' => $uid, 'description' => $description]);
+ }
+
+ /**
+ * Checks the provided hashed_password is available to use for login by the provided user
+ *
+ * @param int $uid User ID
+ * @param string $plaintextPassword
+ * @return bool
+ * @throws \Exception
+ */
+ public static function authenticateUser($uid, $plaintextPassword)
+ {
+ $appSpecificPasswords = self::getListForUser($uid);
+
+ $return = false;
+
+ foreach ($appSpecificPasswords as $appSpecificPassword) {
+ if (password_verify($plaintextPassword, $appSpecificPassword['hashed_password'])) {
+ $fields = ['last_used' => DateTimeFormat::utcNow()];
+ if (password_needs_rehash($appSpecificPassword['hashed_password'], PASSWORD_DEFAULT)) {
+ $fields['hashed_password'] = User::hashPassword($plaintextPassword);
+ }
+
+ self::update($appSpecificPassword['id'], $fields);
+
+ $return |= true;
+ }
+ }
+
+ return $return;
+ }
+
+ /**
+ * Returns a complete list of all recovery hashed_passwords for the provided user, including the used status
+ *
+ * @param int $uid User ID
+ * @return array
+ * @throws \Exception
+ */
+ public static function getListForUser($uid)
+ {
+ $appSpecificPasswordsStmt = DBA::select('2fa_app_specific_password', ['id', 'description', 'hashed_password', 'last_used'], ['uid' => $uid]);
+
+ $appSpecificPasswords = DBA::toArray($appSpecificPasswordsStmt);
+
+ array_walk($appSpecificPasswords, function (&$value) {
+ $value['ago'] = Temporal::getRelativeDate($value['last_used']);
+ });
+
+ return $appSpecificPasswords;
+ }
+
+ /**
+ * Generates a new app specific password for the provided user and hashes it in the database.
+ *
+ * @param int $uid User ID
+ * @param string $description Password description
+ * @return array The new app-specific password data structure with the plaintext password added
+ * @throws \Exception
+ */
+ public static function generateForUser(int $uid, $description)
+ {
+ $Random = (new Random())->size(40);
+
+ $plaintextPassword = $Random->get();
+
+ $generated = DateTimeFormat::utcNow();
+
+ $fields = [
+ 'uid' => $uid,
+ 'description' => $description,
+ 'hashed_password' => User::hashPassword($plaintextPassword),
+ 'generated' => $generated,
+ ];
+
+ DBA::insert('2fa_app_specific_password', $fields);
+
+ $fields['id'] = DBA::lastInsertId();
+ $fields['plaintext_password'] = $plaintextPassword;
+
+ return $fields;
+ }
+
+ private static function update($appSpecificPasswordId, $fields)
+ {
+ return DBA::update('2fa_app_specific_password', $fields, ['id' => $appSpecificPasswordId]);
+ }
+
+ /**
+ * Deletes all the recovery hashed_passwords for the provided user.
+ *
+ * @param int $uid User ID
+ * @return bool
+ * @throws \Exception
+ */
+ public static function deleteAllForUser(int $uid)
+ {
+ return DBA::delete('2fa_app_specific_password', ['uid' => $uid]);
+ }
+
+ /**
+ * @param int $uid
+ * @param int $app_specific_password_id
+ * @return bool
+ * @throws \Exception
+ */
+ public static function deleteForUser(int $uid, int $app_specific_password_id)
+ {
+ return DBA::delete('2fa_app_specific_password', ['id' => $app_specific_password_id, 'uid' => $uid]);
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Model\TwoFactor;
+
+use Friendica\BaseObject;
+use Friendica\Database\DBA;
+use Friendica\Util\DateTimeFormat;
+use PragmaRX\Random\Random;
+use PragmaRX\Recovery\Recovery;
+
+/**
+ * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
+ *
+ * @package Friendica\Model
+ */
+class RecoveryCode extends BaseObject
+{
+ /**
+ * Returns the number of code the provided users can still use to replace a TOTP code
+ *
+ * @param int $uid User ID
+ * @return int
+ * @throws \Exception
+ */
+ public static function countValidForUser($uid)
+ {
+ return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
+ }
+
+ /**
+ * Checks the provided code is available to use for login by the provided user
+ *
+ * @param int $uid User ID
+ * @param string $code
+ * @return bool
+ * @throws \Exception
+ */
+ public static function existsForUser($uid, $code)
+ {
+ return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
+ }
+
+ /**
+ * Returns a complete list of all recovery codes for the provided user, including the used status
+ *
+ * @param int $uid User ID
+ * @return array
+ * @throws \Exception
+ */
+ public static function getListForUser($uid)
+ {
+ $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
+
+ return DBA::toArray($codesStmt);
+ }
+
+ /**
+ * Marks the provided code as used for the provided user.
+ * Returns false if the code doesn't exist for the user or it has been used already.
+ *
+ * @param int $uid User ID
+ * @param string $code
+ * @return bool
+ * @throws \Exception
+ */
+ public static function markUsedForUser($uid, $code)
+ {
+ DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
+
+ return DBA::affectedRows() > 0;
+ }
+
+ /**
+ * Generates a fresh set of recovery codes for the provided user.
+ * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
+ *
+ * @param int $uid User ID
+ * @throws \Exception
+ */
+ public static function generateForUser($uid)
+ {
+ $Random = (new Random())->pattern('[a-z0-9]');
+
+ $RecoveryGenerator = new Recovery($Random);
+
+ $codes = $RecoveryGenerator
+ ->setCount(12)
+ ->setBlocks(2)
+ ->setChars(6)
+ ->lowercase(true)
+ ->toArray();
+
+ $generated = DateTimeFormat::utcNow();
+ foreach ($codes as $code) {
+ DBA::insert('2fa_recovery_codes', [
+ 'uid' => $uid,
+ 'code' => $code,
+ 'generated' => $generated
+ ]);
+ }
+ }
+
+ /**
+ * Deletes all the recovery codes for the provided user.
+ *
+ * @param int $uid User ID
+ * @throws \Exception
+ */
+ public static function deleteForUser($uid)
+ {
+ DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
+ }
+
+ /**
+ * Replaces the existing recovery codes for the provided user by a freshly generated set.
+ *
+ * @param int $uid User ID
+ * @throws \Exception
+ */
+ public static function regenerateForUser($uid)
+ {
+ self::deleteForUser($uid);
+ self::generateForUser($uid);
+ }
+}
+++ /dev/null
-<?php
-
-namespace Friendica\Model;
-
-use Friendica\BaseObject;
-use Friendica\Database\DBA;
-use Friendica\Util\DateTimeFormat;
-use PragmaRX\Random\Random;
-use PragmaRX\Recovery\Recovery;
-
-/**
- * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
- *
- * @package Friendica\Model
- */
-class TwoFactorRecoveryCode extends BaseObject
-{
- /**
- * Returns the number of code the provided users can still use to replace a TOTP code
- *
- * @param int $uid User ID
- * @return int
- * @throws \Exception
- */
- public static function countValidForUser($uid)
- {
- return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
- }
-
- /**
- * Checks the provided code is available to use for login by the provided user
- *
- * @param int $uid User ID
- * @param string $code
- * @return bool
- * @throws \Exception
- */
- public static function existsForUser($uid, $code)
- {
- return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
- }
-
- /**
- * Returns a complete list of all recovery codes for the provided user, including the used status
- *
- * @param int $uid User ID
- * @return array
- * @throws \Exception
- */
- public static function getListForUser($uid)
- {
- $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
-
- return DBA::toArray($codesStmt);
- }
-
- /**
- * Marks the provided code as used for the provided user.
- * Returns false if the code doesn't exist for the user or it has been used already.
- *
- * @param int $uid User ID
- * @param string $code
- * @return bool
- * @throws \Exception
- */
- public static function markUsedForUser($uid, $code)
- {
- DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
-
- return DBA::affectedRows() > 0;
- }
-
- /**
- * Generates a fresh set of recovery codes for the provided user.
- * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
- *
- * @param int $uid User ID
- * @throws \Exception
- */
- public static function generateForUser($uid)
- {
- $Random = (new Random())->pattern('[a-z0-9]');
-
- $RecoveryGenerator = new Recovery($Random);
-
- $codes = $RecoveryGenerator
- ->setCount(12)
- ->setBlocks(2)
- ->setChars(6)
- ->lowercase(true)
- ->toArray();
-
- $generated = DateTimeFormat::utcNow();
- foreach ($codes as $code) {
- DBA::insert('2fa_recovery_codes', [
- 'uid' => $uid,
- 'code' => $code,
- 'generated' => $generated
- ]);
- }
- }
-
- /**
- * Deletes all the recovery codes for the provided user.
- *
- * @param int $uid User ID
- * @throws \Exception
- */
- public static function deleteForUser($uid)
- {
- DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
- }
-
- /**
- * Replaces the existing recovery codes for the provided user by a freshly generated set.
- *
- * @param int $uid User ID
- * @throws \Exception
- */
- public static function regenerateForUser($uid)
- {
- self::deleteForUser($uid);
- self::generateForUser($uid);
- }
-}
use Friendica\Core\Worker;
use Friendica\Database\DBA;
use Friendica\Model\Photo;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
use Friendica\Object\Image;
use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat;
/**
* Authenticate a user with a clear text password
*
- * @brief Authenticate a user with a clear text password
- * @param mixed $user_info
+ * @brief Authenticate a user with a clear text password
+ * @param mixed $user_info
* @param string $password
+ * @param bool $third_party
* @return int|boolean
* @deprecated since version 3.6
- * @see User::getIdFromPasswordAuthentication()
+ * @see User::getIdFromPasswordAuthentication()
*/
- public static function authenticate($user_info, $password)
+ public static function authenticate($user_info, $password, $third_party = false)
{
try {
- return self::getIdFromPasswordAuthentication($user_info, $password);
+ return self::getIdFromPasswordAuthentication($user_info, $password, $third_party);
} catch (Exception $ex) {
return false;
}
* Returns the user id associated with a successful password authentication
*
* @brief Authenticate a user with a clear text password
- * @param mixed $user_info
+ * @param mixed $user_info
* @param string $password
+ * @param bool $third_party
* @return int User Id if authentication is successful
* @throws Exception
*/
- public static function getIdFromPasswordAuthentication($user_info, $password)
+ public static function getIdFromPasswordAuthentication($user_info, $password, $third_party = false)
{
$user = self::getAuthenticationInfo($user_info);
- if (strpos($user['password'], '$') === false) {
+ if ($third_party && PConfig::get($user['uid'], '2fa', 'verified')) {
+ // Third-party apps can't verify two-factor authentication, we use app-specific passwords instead
+ if (AppSpecificPassword::authenticateUser($user['uid'], $password)) {
+ return $user['uid'];
+ }
+ } elseif (strpos($user['password'], '$') === false) {
//Legacy hash that has not been replaced by a new hash yet
if (self::hashPasswordLegacy($password) === $user['password']) {
self::updatePasswordHashed($user['uid'], self::hashPassword($password));
// get basic installation information and save them to the config cache
$configCache = $a->getConfigCache();
- self::$installer->setUpCache($configCache, BasePath::create($a->getBasePath(), $_SERVER));
+ $basePath = new BasePath($a->getBasePath());
+ self::$installer->setUpCache($configCache, $basePath->getPath());
// We overwrite current theme css, because during install we may not have a working mod_rewrite
// so we may not have a css at all. Here we set a static css file for the install procedure pages
--- /dev/null
+<?php
+
+
+namespace Friendica\Module\Settings\TwoFactor;
+
+
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig;
+use Friendica\Core\Renderer;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
+use Friendica\Module\BaseSettingsModule;
+use Friendica\Module\Login;
+
+/**
+ * // Page 5: 2FA enabled, app-specific password generation
+ *
+ * @package Friendica\Module\TwoFactor
+ */
+class AppSpecific extends BaseSettingsModule
+{
+ private static $appSpecificPassword = null;
+
+ public static function init()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+ if (!$verified) {
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+
+ if (!self::checkFormSecurityToken('settings_2fa_password', 't')) {
+ notice(L10n::t('Please enter your password to access this page.'));
+ self::getApp()->internalRedirect('settings/2fa');
+ }
+ }
+
+ public static function post()
+ {
+ if (!local_user()) {
+ return;
+ }
+
+ if (!empty($_POST['action'])) {
+ self::checkFormSecurityTokenRedirectOnError('settings/2fa/app_specific', 'settings_2fa_app_specific');
+
+ switch ($_POST['action']) {
+ case 'generate':
+ $description = $_POST['description'] ?? '';
+ if (empty($description)) {
+ notice(L10n::t('App-specific password generation failed: The description is empty.'));
+ self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ } elseif (AppSpecificPassword::checkDuplicateForUser(local_user(), $description)) {
+ notice(L10n::t('App-specific password generation failed: This description already exists.'));
+ self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ } else {
+ self::$appSpecificPassword = AppSpecificPassword::generateForUser(local_user(), $_POST['description'] ?? '');
+ notice(L10n::t('New app-specific password generated.'));
+ }
+
+ break;
+ case 'revoke_all' :
+ AppSpecificPassword::deleteAllForUser(local_user());
+ notice(L10n::t('App-specific passwords successfully revoked.'));
+ self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ break;
+ }
+ }
+
+ if (!empty($_POST['revoke_id'])) {
+ self::checkFormSecurityTokenRedirectOnError('settings/2fa/app_specific', 'settings_2fa_app_specific');
+
+ if (AppSpecificPassword::deleteForUser(local_user(), $_POST['revoke_id'])) {
+ notice(L10n::t('App-specific password successfully revoked.'));
+ }
+
+ self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ }
+
+ public static function content()
+ {
+ if (!local_user()) {
+ return Login::form('settings/2fa/app_specific');
+ }
+
+ parent::content();
+
+ $appSpecificPasswords = AppSpecificPassword::getListForUser(local_user());
+
+ return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/app_specific.tpl'), [
+ '$form_security_token' => self::getFormSecurityToken('settings_2fa_app_specific'),
+ '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'),
+
+ '$title' => L10n::t('Two-factor app-specific passwords'),
+ '$help_label' => L10n::t('Help'),
+ '$message' => L10n::t('<p>App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don\'t support two-factor authentication.</p>'),
+ '$generated_message' => L10n::t('Make sure to copy your new app-specific password now. You won’t be able to see it again!'),
+ '$generated_app_specific_password' => self::$appSpecificPassword,
+
+ '$description_label' => L10n::t('Description'),
+ '$last_used_label' => L10n::t('Last Used'),
+ '$revoke_label' => L10n::t('Revoke'),
+ '$revoke_all_label' => L10n::t('Revoke All'),
+
+ '$app_specific_passwords' => $appSpecificPasswords,
+ '$generate_message' => L10n::t('When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it.'),
+ '$generate_title' => L10n::t('Generate new app-specific password'),
+ '$description_placeholder_label' => L10n::t('Friendiqa on my Fairphone 2...'),
+ '$generate_label' => L10n::t('Generate'),
+ ]);
+ }
+}
use Friendica\Core\PConfig;
use Friendica\Core\Renderer;
use Friendica\Core\Session;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
+use Friendica\Model\TwoFactor\RecoveryCode;
use Friendica\Model\User;
use Friendica\Module\BaseSettingsModule;
use Friendica\Module\Login;
break;
case 'disable':
if ($has_secret) {
- TwoFactorRecoveryCode::deleteForUser(local_user());
+ RecoveryCode::deleteForUser(local_user());
PConfig::delete(local_user(), '2fa', 'secret');
PConfig::delete(local_user(), '2fa', 'verified');
Session::remove('2fa');
self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
}
break;
+ case 'app_specific':
+ if ($has_secret) {
+ self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+ }
+ break;
case 'configure':
if (!$verified) {
self::getApp()->internalRedirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password'));
'$recovery_codes_title' => L10n::t('Recovery codes'),
'$recovery_codes_remaining' => L10n::t('Remaining valid codes'),
- '$recovery_codes_count' => TwoFactorRecoveryCode::countValidForUser(local_user()),
+ '$recovery_codes_count' => RecoveryCode::countValidForUser(local_user()),
'$recovery_codes_message' => L10n::t('<p>These one-use codes can replace an authenticator app code in case you have lost access to it.</p>'),
+ '$app_specific_passwords_title' => L10n::t('App-specific passwords'),
+ '$app_specific_passwords_remaining' => L10n::t('Generated app-specific passwords'),
+ '$app_specific_passwords_count' => AppSpecificPassword::countForUser(local_user()),
+ '$app_specific_passwords_message' => L10n::t('<p>These randomly generated passwords allow you to authenticate on apps not supporting two-factor authentication.</p>'),
+
'$action_title' => L10n::t('Actions'),
'$password' => ['password', L10n::t('Current password:'), '', L10n::t('You need to provide your current password to change two-factor authentication settings.'), 'required', 'autofocus'],
'$enable_label' => L10n::t('Enable two-factor authentication'),
'$disable_label' => L10n::t('Disable two-factor authentication'),
'$recovery_codes_label' => L10n::t('Show recovery codes'),
+ '$app_specific_passwords_label' => L10n::t('Manage app-specific passwords'),
'$configure_label' => L10n::t('Finish app configuration'),
]);
}
use Friendica\Core\L10n;
use Friendica\Core\PConfig;
use Friendica\Core\Renderer;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\RecoveryCode;
use Friendica\Module\BaseSettingsModule;
use Friendica\Module\Login;
self::checkFormSecurityTokenRedirectOnError('settings/2fa/recovery', 'settings_2fa_recovery');
if ($_POST['action'] == 'regenerate') {
- TwoFactorRecoveryCode::regenerateForUser(local_user());
+ RecoveryCode::regenerateForUser(local_user());
notice(L10n::t('New recovery codes successfully generated.'));
self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
}
parent::content();
- if (!TwoFactorRecoveryCode::countValidForUser(local_user())) {
- TwoFactorRecoveryCode::generateForUser(local_user());
+ if (!RecoveryCode::countValidForUser(local_user())) {
+ RecoveryCode::generateForUser(local_user());
}
- $recoveryCodes = TwoFactorRecoveryCode::getListForUser(local_user());
+ $recoveryCodes = RecoveryCode::getListForUser(local_user());
$verified = PConfig::get(local_user(), '2fa', 'verified');
use Friendica\Core\L10n;
use Friendica\Core\Renderer;
use Friendica\Core\Session;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\RecoveryCode;
/**
* // Page 1a: Recovery code verification
$recovery_code = defaults($_POST, 'recovery_code', '');
- if (TwoFactorRecoveryCode::existsForUser(local_user(), $recovery_code)) {
- TwoFactorRecoveryCode::markUsedForUser(local_user(), $recovery_code);
+ if (RecoveryCode::existsForUser(local_user(), $recovery_code)) {
+ RecoveryCode::markUsedForUser(local_user(), $recovery_code);
Session::set('2fa', true);
- notice(L10n::t('Remaining recovery codes: %d', TwoFactorRecoveryCode::countValidForUser(local_user())));
+ notice(L10n::t('Remaining recovery codes: %d', RecoveryCode::countValidForUser(local_user())));
// Resume normal login workflow
Session::setAuthenticatedForUser($a, $a->user, true, true);
}
if ($host == 'twitter.com') {
- return ["network" => Protocol::TWITTER];
+ return self::twitter($uri);
}
$lrdd = self::hostMeta($host);
$nick = substr($uri, 0, strpos($uri, '@'));
if (strpos($uri, '@twitter.com')) {
- return ["network" => Protocol::TWITTER];
+ return self::twitter($uri);
}
$lrdd = self::hostMeta($host);
return $data;
}
+ /**
+ * @brief Check for twitter contact
+ *
+ * @param string $uri
+ *
+ * @return array twitter data
+ */
+ private static function twitter($uri)
+ {
+ if (preg_match('=(.*)@twitter.com=i', $uri, $matches)) {
+ $nick = $matches[1];
+ } elseif (preg_match('=https?://twitter.com/(.*)=i', $uri, $matches)) {
+ $nick = $matches[1];
+ } else {
+ return [];
+ }
+
+ $data = [];
+ $data['url'] = 'https://twitter.com/' . $nick;
+ $data['addr'] = $nick . '@twitter.com';
+ $data['nick'] = $data['name'] = $nick;
+ $data['network'] = Protocol::TWITTER;
+ $data['baseurl'] = 'https://twitter.com';
+
+ $curlResult = Network::curl($data['url'], false);
+ if ($curlResult->isSuccess()) {
+ return $data;
+ }
+ return [];
+ }
+
/**
* @brief Check page for feed link
*
/**
* Fetches missing posts
*
- * @param $url
- * @param $child
+ * @param string $url message URL
+ * @param array $child activity array with the child of this message
+ * @return boolean success
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function fetchMissingActivity($url, $child = [])
$object = ActivityPub::fetchContent($url, $uid);
if (empty($object)) {
Logger::log('Activity ' . $url . ' was not fetchable, aborting.');
- return;
+ return false;
}
if (empty($object['id'])) {
Logger::log('Activity ' . $url . ' has got not id, aborting. ' . json_encode($object));
- return;
+ return false;
}
if (!empty($child['author'])) {
ActivityPub\Receiver::processActivity($ldactivity);
Logger::log('Activity ' . $url . ' had been fetched and processed.');
+
+ return true;
}
/**
return $msg;
}
+ /**
+ * @brief Fetches an item with a given URL
+ *
+ * @param string $url the message url
+ *
+ * @return int the message id of the stored message or false
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
+ */
+ public static function fetchByURL($url, $uid = 0)
+ {
+ // Check for Diaspora (and Friendica) typical paths
+ if (!preg_match("=(https?://.+)/(?:posts|display)/([a-zA-Z0-9-_@.:%]+[a-zA-Z0-9])=i", $url, $matches)) {
+ return false;
+ }
+
+ $guid = urldecode($matches[2]);
+
+ $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
+ if (DBA::isResult($item)) {
+ return $item['id'];
+ }
+
+ self::storeByGuid($guid, $matches[1], $uid);
+
+ $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
+ if (DBA::isResult($item)) {
+ return $item['id'];
+ } else {
+ return false;
+ }
+ }
+
/**
* @brief Fetches the item record of a given guid
*
class BasePath
{
+ /**
+ * @var string
+ */
+ private $baseDir;
+ /**
+ * @var array
+ */
+ private $server;
+
+ /**
+ * @param string|null $baseDir The default base path
+ * @param array $server server arguments
+ */
+ public function __construct(string $baseDir, array $server = [])
+ {
+ $this->baseDir = $baseDir;
+ $this->server = $server;
+ }
+
/**
* @brief Returns the base filesystem path of the App
*
* It first checks for the internal variable, then for DOCUMENT_ROOT and
* finally for PWD
*
- * @param string|null $basePath The default base path
- * @param array $server server arguments
- *
* @return string
*
* @throws \Exception if directory isn't usable
*/
- public static function create($basePath, array $server = [])
+ public function getPath()
{
- if ((!$basePath || !is_dir($basePath)) && !empty($server['DOCUMENT_ROOT'])) {
- $basePath = $server['DOCUMENT_ROOT'];
+ $baseDir = $this->baseDir;
+ $server = $this->server;
+
+ if ((!$baseDir || !is_dir($baseDir)) && !empty($server['DOCUMENT_ROOT'])) {
+ $baseDir = $server['DOCUMENT_ROOT'];
}
- if ((!$basePath || !is_dir($basePath)) && !empty($server['PWD'])) {
- $basePath = $server['PWD'];
+ if ((!$baseDir || !is_dir($baseDir)) && !empty($server['PWD'])) {
+ $baseDir = $server['PWD'];
}
- $basePath = self::getRealPath($basePath);
+ $baseDir = self::getRealPath($baseDir);
- if (!is_dir($basePath)) {
- throw new \Exception(sprintf('\'%s\' is not a valid basepath', $basePath));
+ if (!is_dir($baseDir)) {
+ throw new \Exception(sprintf('\'%s\' is not a valid basepath', $baseDir));
}
- return $basePath;
+ return $baseDir;
}
/**
namespace Friendica\Util;
use Exception;
-use Friendica\App;
use Friendica\Core\Addon;
use Friendica\Core\Config\Cache\ConfigCache;
*/
const SAMPLE_END = '-sample';
- /**
- * @var App\Mode
- */
- private $appMode;
/**
* @var string
*/
*/
private $staticDir;
- public function __construct($baseDir, App\Mode $mode)
+ public function __construct(string $basePath)
{
- $this->baseDir = $baseDir;
- $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::CONFIG_DIR;
- $this->staticDir = $baseDir . DIRECTORY_SEPARATOR . self::STATIC_DIR;
- $this->appMode = $mode;
+ $this->baseDir = $basePath;
+ $this->configDir = $this->baseDir . DIRECTORY_SEPARATOR . self::CONFIG_DIR;
+ $this->staticDir = $this->baseDir . DIRECTORY_SEPARATOR . self::STATIC_DIR;
}
/**
$this->loadCoreConfig($config);
// In case of install mode, add the found basepath (because there isn't a basepath set yet
- if (!$raw && ($this->appMode->isInstall() || empty($config->get('system', 'basepath')))) {
+ if (!$raw && empty($config->get('system', 'basepath'))) {
// Setting at least the basepath we know
$config->set('system', 'basepath', $this->baseDir);
}
$aUser = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'], ['nickname' => $sUser]);
if (DBA::isResult($aUser)) {
$uid = $aUser['uid'];
- $success = User::authenticate($aUser, $aCommand[3]);
+ $success = User::authenticate($aUser, $aCommand[3], true);
$Error = $success === false;
} else {
$this->writeLog(LOG_WARNING, 'user not found: ' . $sUser);
namespace Friendica\Util;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Updates the enabling of the current profiler
*
- * @param bool $enabled
- * @param bool $renderTime
+ * @param Configuration $config
*/
- public function update($enabled = false, $renderTime = false)
+ public function update(Configuration $config)
{
- $this->enabled = $enabled;
- $this->rendertime = $renderTime;
+ $this->enabled = $config->get('system', 'profiler');
+ $this->rendertime = $config->get('rendertime', 'callstack');
}
/**
- * @param bool $enabled True, if the Profiler is enabled
- * @param bool $renderTime True, if the Profiler should measure the whole rendertime including functions
+ * @param ConfigCache $configCache The configuration cache
*/
- public function __construct($enabled = false, $renderTime = false)
+ public function __construct(ConfigCache $configCache)
{
- $this->enabled = $enabled;
- $this->rendertime = $renderTime;
+ $this->enabled = $configCache->get('system', 'profiler');
+ $this->rendertime = $configCache->get('rendertime', 'callstack');
$this->reset();
}
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
- define('DB_UPDATE_VERSION', 1319);
+ define('DB_UPDATE_VERSION', 1320);
}
return [
+ "2fa_app_specific_password" => [
+ "comment" => "Two-factor app-specific _password",
+ "fields" => [
+ "id" => ["type" => "mediumint unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Password ID for revocation"],
+ "uid" => ["type" => "mediumint unsigned", "not null" => "1", "relation" => ["user" => "uid"], "comment" => "User ID"],
+ "description" => ["type" => "varchar(255)", "comment" => "Description of the usage of the password"],
+ "hashed_password" => ["type" => "varchar(255)", "not null" => "1", "primary" => "1", "comment" => "Hashed password"],
+ "generated" => ["type" => "datetime", "not null" => "1", "comment" => "Datetime the password was generated"],
+ "last_used" => ["type" => "datetime", "comment" => "Datetime the password was last used"],
+ ],
+ "indexes" => [
+ "PRIMARY" => ["id"],
+ "uid_description" => ["uid", "description"],
+ ]
+ ],
"2fa_recovery_codes" => [
"comment" => "Two-factor authentication recovery codes",
"fields" => [
--- /dev/null
+<?php
+
+use Dice\Dice;
+use Friendica\App;
+use Friendica\Core\Config;
+use Friendica\Database\Database;
+use Friendica\Factory;
+use Friendica\Util;
+use Psr\Log\LoggerInterface;
+
+/**
+ * The configuration defines "complex" dependencies inside Friendica
+ * So this classes shouldn't be simple or their dependencies are already defined here.
+ *
+ * This kind of dependencies are NOT required to be defined here:
+ * - $a = new ClassA(new ClassB());
+ * - $a = new ClassA();
+ * - $a = new ClassA(Configuration $configuration);
+ *
+ * This kind of dependencies SHOULD be defined here:
+ * - $a = new ClassA();
+ * $b = $a->create();
+ *
+ * - $a = new ClassA($creationPassedVariable);
+ *
+ */
+return [
+ '$basepath' => [
+ 'instanceOf' => Util\BasePath::class,
+ 'call' => [
+ ['getPath', [], Dice::CHAIN_CALL],
+ ],
+ 'constructParams' => [
+ dirname(__FILE__, 2),
+ $_SERVER
+ ]
+ ],
+ Util\BasePath::class => [
+ 'shared' => true,
+ 'constructParams' => [
+ dirname(__FILE__, 2),
+ $_SERVER
+ ]
+ ],
+ Util\ConfigFileLoader::class => [
+ 'shared' => true,
+ 'constructParams' => [
+ [Dice::INSTANCE => '$basepath'],
+ ],
+ ],
+ Config\Cache\ConfigCache::class => [
+ 'instanceOf' => Factory\ConfigFactory::class,
+ 'call' => [
+ ['createCache', [], Dice::CHAIN_CALL],
+ ],
+ 'shared' => true,
+ ],
+ App\Mode::class => [
+ 'call' => [
+ ['determine', [], Dice::CHAIN_CALL],
+ ],
+ // marks the result as shared for other creations, so there's just
+ // one instance for the whole execution
+ 'shared' => true,
+ ],
+ Config\Configuration::class => [
+ 'shared' => true,
+ 'instanceOf' => Factory\ConfigFactory::class,
+ 'call' => [
+ ['createConfig', [], Dice::CHAIN_CALL],
+ ],
+ ],
+ Config\PConfiguration::class => [
+ 'shared' => true,
+ 'instanceOf' => Factory\ConfigFactory::class,
+ 'call' => [
+ ['createPConfig', [], Dice::CHAIN_CALL],
+ ]
+ ],
+ Database::class => [
+ 'shared' => true,
+ 'constructParams' => [
+ [DICE::INSTANCE => \Psr\Log\NullLogger::class],
+ $_SERVER,
+ ],
+ ],
+ /**
+ * Creates the Util\BaseURL
+ *
+ * Same as:
+ * $baseURL = new Util\BaseURL($configuration, $_SERVER);
+ */
+ Util\BaseURL::class => [
+ 'shared' => true,
+ 'constructParams' => [
+ $_SERVER,
+ ],
+ ],
+ /**
+ * Create a Logger, which implements the LoggerInterface
+ *
+ * Same as:
+ * $loggerFactory = new Factory\LoggerFactory();
+ * $logger = $loggerFactory->create($channel, $configuration, $profiler);
+ *
+ * Attention1: We can use DICE for detecting dependencies inside "chained" calls too
+ * Attention2: The variable "$channel" is passed inside the creation of the dependencies per:
+ * $app = $dice->create(App::class, [], ['$channel' => 'index']);
+ * and is automatically passed as an argument with the same name
+ */
+ LoggerInterface::class => [
+ 'shared' => true,
+ 'instanceOf' => Factory\LoggerFactory::class,
+ 'call' => [
+ ['create', [], Dice::CHAIN_CALL],
+ ],
+ ],
+ '$devLogger' => [
+ 'shared' => true,
+ 'instanceOf' => Factory\LoggerFactory::class,
+ 'call' => [
+ ['createDev', [], Dice::CHAIN_CALL],
+ ]
+ ]
+];
use Friendica\Core\Config\Cache\ConfigCache;
use Friendica\Database\Database;
use Friendica\Factory\ConfigFactory;
-use Friendica\Factory\DBFactory;
-use Friendica\Factory\ProfilerFactory;
use Friendica\Util\BasePath;
use Friendica\Util\ConfigFileLoader;
use Friendica\Util\Profiler;
use PHPUnit\DbUnit\DataSet\YamlDataSet;
use PHPUnit\DbUnit\TestCaseTrait;
use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
+use Psr\Log\NullLogger;
require_once __DIR__ . '/../boot.php';
{
parent::setUpBeforeClass();
- self::$basePath = BasePath::create(dirname(__DIR__));
- self::$mode = new Mode(self::$basePath);
- $configLoader = new ConfigFileLoader(self::$basePath, self::$mode);
- self::$configCache = ConfigFactory::createCache($configLoader);
- self::$profiler = ProfilerFactory::create(self::$configCache);
- self::$dba = DBFactory::init(self::$configCache, self::$profiler, $_SERVER);
+ self::$basePath = new BasePath(dirname(__DIR__));
+ $configLoader = new ConfigFileLoader(self::$basePath->getPath());
+ $configFactory = new ConfigFactory();
+ self::$configCache = $configFactory->createCache($configLoader);
+ self::$profiler = new Profiler(self::$configCache);
+ self::$dba = new Database(self::$configCache, self::$profiler, new NullLogger(), $_SERVER);
+ self::$mode = new Mode(self::$basePath, self::$dba, self::$configCache);
}
/**
--- /dev/null
+<?php
+
+namespace functional;
+
+use Dice\Dice;
+use Friendica\App;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
+use Friendica\Database\Database;
+use Friendica\Test\Util\VFSTrait;
+use Friendica\Util\BasePath;
+use Friendica\Util\ConfigFileLoader;
+use Friendica\Util\Profiler;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class dependencyCheck extends TestCase
+{
+ use VFSTrait;
+
+ /**
+ * @var Dice
+ */
+ private $dice;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->setUpVfsDir();
+
+ $this->dice = new Dice();
+ $this->dice = $this->dice->addRules(include __DIR__ . '/../../static/dependencies.config.php');
+ }
+
+ /**
+ * Test the creation of the BasePath
+ */
+ public function testBasePath()
+ {
+ /** @var BasePath $basePath */
+ $basePath = $this->dice->create(BasePath::class, [$this->root->url()]);
+
+ $this->assertInstanceOf(BasePath::class, $basePath);
+ $this->assertEquals($this->root->url(), $basePath->getPath());
+ }
+
+ /**
+ * Test the initial config cache
+ * Should not need any other files
+ */
+ public function testConfigFileLoader()
+ {
+ /** @var ConfigFileLoader $configFileLoader */
+ $configFileLoader = $this->dice->create(ConfigFileLoader::class);
+
+ $this->assertInstanceOf(ConfigFileLoader::class, $configFileLoader);
+
+ $configCache = new ConfigCache();
+ $configFileLoader->setupCache($configCache);
+
+ $this->assertNotEmpty($configCache->getAll());
+ $this->assertArrayHasKey('database', $configCache->getAll());
+ $this->assertArrayHasKey('system', $configCache->getAll());
+ }
+
+ /**
+ * Test the construction of a profiler class with DI
+ */
+ public function testProfiler()
+ {
+ /** @var Profiler $profiler */
+ $profiler = $this->dice->create(Profiler::class);
+
+ $this->assertInstanceOf(Profiler::class, $profiler);
+
+ $configCache = new ConfigCache([
+ 'system' => [
+ 'profiler' => true,
+ ],
+ 'rendertime' => [
+ 'callstack' => true,
+ ]
+ ]);
+
+ $profiler = $this->dice->create(Profiler::class, [$configCache]);
+
+ $this->assertInstanceOf(Profiler::class, $profiler);
+ $this->assertTrue($profiler->isRendertime());
+ }
+
+ public function testDatabase()
+ {
+ /** @var Database $database */
+ $database = $this->dice->create(Database::class);
+
+ $this->assertInstanceOf(Database::class, $database);
+ $this->assertTrue($database->connected());
+ }
+
+ public function testAppMode()
+ {
+ /** @var App\Mode $mode */
+ $mode = $this->dice->create(App\Mode::class);
+
+ $this->assertInstanceOf(App\Mode::class, $mode);
+
+ $this->assertTrue($mode->isNormal());
+ }
+
+ public function testConfiguration()
+ {
+ /** @var Configuration $config */
+ $config = $this->dice->create(Configuration::class);
+
+ $this->assertInstanceOf(Configuration::class, $config);
+
+ $this->assertNotEmpty($config->get('database', 'username'));
+ }
+
+ public function testLogger()
+ {
+ /** @var LoggerInterface $logger */
+ $logger = $this->dice->create(LoggerInterface::class, ['test']);
+
+ $this->assertInstanceOf(LoggerInterface::class, $logger);
+ }
+
+ public function testDevLogger()
+ {
+ /** @var LoggerInterface $logger */
+ $logger = $this->dice->create('$devLogger', ['dev']);
+
+ self::assertInstanceOf(LoggerInterface::class, $logger);
+ }
+}
use Friendica\Factory;
use Friendica\Network\HTTPException;
use Friendica\Util\BaseURL;
-use Friendica\Util\ConfigFileLoader;
use Monolog\Handler\TestHandler;
require_once __DIR__ . '/../../include/api.php';
public function setUp()
{
$configModel = new \Friendica\Model\Config\Config(self::$dba);
- $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+ $configFactory = new Factory\ConfigFactory();
+ $config = $configFactory->createConfig(self::$configCache, $configModel);
$pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
- Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
- $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+ $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+ $loggerFactory = new Factory\LoggerFactory();
+ $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
$baseUrl = new BaseURL($config, $_SERVER);
$router = new App\Router();
- $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+ $l10n = new L10n($config,
self::$dba,
$logger);
$this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
use Friendica\App\Mode;
use Friendica\Core\Config;
+use Friendica\Database\Database;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\DBAMockTrait;
use Friendica\Test\Util\VFSTrait;
+use Friendica\Util\BasePath;
+use Mockery\MockInterface;
class ModeTest extends MockedTest
{
use VFSTrait;
use DBAMockTrait;
+ /**
+ * @var BasePath|MockInterface
+ */
+ private $basePathMock;
+
+ /**
+ * @var Database|MockInterface
+ */
+ private $databaseMock;
+
+ /**
+ * @var Config\Cache\ConfigCache|MockInterface
+ */
+ private $configCacheMock;
+
public function setUp()
{
parent::setUp();
$this->setUpVfsDir();
+
+ $this->basePathMock = \Mockery::mock(BasePath::class);
+ $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once();
+
+ $this->databaseMock = \Mockery::mock(Database::class);
+ $this->configCacheMock = \Mockery::mock(Config\Cache\ConfigCache::class);
}
public function testItEmpty()
{
- $mode = new Mode($this->root->url());
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$this->assertTrue($mode->isInstall());
$this->assertFalse($mode->isNormal());
}
public function testWithoutConfig()
{
- $mode = new Mode($this->root->url());
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$this->assertTrue($this->root->hasChild('config/local.config.php'));
$this->assertFalse($mode->has(Mode::LOCALCONFIGPRESENT));
}
- /**
- * @runInSeparateProcess
- * @preserveGlobalState disabled
- */
public function testWithoutDatabase()
{
- $this->mockConnected(false, 1);
+ $this->databaseMock->shouldReceive('connected')->andReturn(false)->once();
- $mode = new Mode($this->root->url());
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$mode->determine();
$this->assertFalse($mode->isNormal());
$this->assertFalse($mode->has(Mode::DBAVAILABLE));
}
- /**
- * @runInSeparateProcess
- * @preserveGlobalState disabled
- */
public function testWithoutDatabaseSetup()
{
- $this->mockConnected(true, 1);
- $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', false, 1);
+ $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+ $this->databaseMock->shouldReceive('fetchFirst')
+ ->with('SHOW TABLES LIKE \'config\'')->andReturn(false)->once();
- $mode = new Mode($this->root->url());
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$mode->determine();
$this->assertFalse($mode->isNormal());
$this->assertTrue($mode->has(Mode::LOCALCONFIGPRESENT));
}
- /**
- * @runInSeparateProcess
- * @preserveGlobalState disabled
- */
public function testWithMaintenanceMode()
{
- $this->mockConnected(true, 1);
- $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-
- $config = \Mockery::mock(Config\Configuration::class);
- $config
- ->shouldReceive('get')
- ->with('system', 'maintenance', null, false)
- ->andReturn(true)
- ->once();
- // Initialize empty Config
- Config::init($config);
-
- $mode = new Mode($this->root->url());
+ $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+ $this->databaseMock->shouldReceive('fetchFirst')
+ ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+ $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+ ->andReturn(true)->once();
+
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$mode->determine();
$this->assertFalse($mode->isNormal());
$this->assertFalse($mode->has(Mode::MAINTENANCEDISABLED));
}
+ public function testNormalMode()
+ {
+ $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+ $this->databaseMock->shouldReceive('fetchFirst')
+ ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+ $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+ ->andReturn(false)->once();
+ $this->databaseMock->shouldReceive('selectFirst')
+ ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])
+ ->andReturn(['v' => null])->once();
+
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
+ $mode->determine();
+
+ $this->assertTrue($mode->isNormal());
+ $this->assertFalse($mode->isInstall());
+
+ $this->assertTrue($mode->has(Mode::DBCONFIGAVAILABLE));
+ $this->assertTrue($mode->has(Mode::MAINTENANCEDISABLED));
+ }
+
/**
- * @runInSeparateProcess
- * @preserveGlobalState disabled
+ * Test explicit disabled maintenance (in case you manually disable it)
*/
- public function testNormalMode()
+ public function testDisabledMaintenance()
{
- $this->mockConnected(true, 1);
- $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-
- $config = \Mockery::mock(Config\Configuration::class);
- $config
- ->shouldReceive('get')
- ->with('system', 'maintenance', null, false)
- ->andReturn(false)
- ->once();
- // Initialize empty Config
- Config::init($config);
-
- $mode = new Mode($this->root->url());
+ $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+ $this->databaseMock->shouldReceive('fetchFirst')
+ ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+ $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+ ->andReturn(false)->once();
+ $this->databaseMock->shouldReceive('selectFirst')
+ ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])
+ ->andReturn(['v' => '0'])->once();
+
+ $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
$mode->determine();
$this->assertTrue($mode->isNormal());
public function setUp()
{
$configModel = new \Friendica\Model\Config\Config(self::$dba);
- $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+ $configFactory = new Factory\ConfigFactory();
+ $config = $configFactory->createConfig(self::$configCache, $configModel);
$pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
- Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
- $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+ $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+ $loggerFactory = new Factory\LoggerFactory();
+ $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
$baseUrl = new BaseURL($config, $_SERVER);
$router = new App\Router();
- $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+ $l10n = new L10n($config,
self::$dba,
$logger);
$this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
public function setUp()
{
$configModel = new Config(self::$dba);
- $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+ $configFactory = new Factory\ConfigFactory();
+ $config = $configFactory->createConfig(self::$configCache, $configModel);
$pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
- Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
- $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+ $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+ $loggerFactory = new Factory\LoggerFactory();
+ $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
$baseUrl = new BaseURL($config, $_SERVER);
$router = new App\Router();
- $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+ $l10n = new L10n($config,
self::$dba,
$logger);
$this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
*/
public function testDetermineBasePath(array $server, $input, $output)
{
- $this->assertEquals($output, BasePath::create($input, $server));
+ $basepath = new BasePath($input, $server);
+ $this->assertEquals($output, $basepath->getPath());
}
/**
*/
public function testFailedBasePath()
{
- BasePath::create('/now23452sgfgas', []);
+ $basepath = new BasePath('/now23452sgfgas', []);
+ $basepath->getPath();
}
}
namespace Friendica\Test\src\Util\Config;
-use Friendica\App;
use Friendica\Core\Config\Cache\ConfigCache;
use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
use Friendica\Util\ConfigFileLoader;
-use Mockery\MockInterface;
use org\bovigo\vfs\vfsStream;
class ConfigFileLoaderTest extends MockedTest
{
use VFSTrait;
- /**
- * @var App\Mode|MockInterface
- */
- private $mode;
-
protected function setUp()
{
parent::setUp();
$this->setUpVfsDir();
-
- $this->mode = \Mockery::mock(App\Mode::class);
- $this->mode->shouldReceive('isInstall')->andReturn(true);
}
/**
*/
public function testLoadConfigFiles()
{
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $this->delConfigFile('local.config.php');
+
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('config'))
->setContent('<?php return true;');
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('config'))
->setContent(file_get_contents($file));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('config'))
->setContent(file_get_contents($file));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root)
->setContent(file_get_contents($file));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('addon')->getChild('test')->getChild('config'))
->setContent(file_get_contents($file));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$conf = $configFileLoader->loadAddonConfig('test');
->at($this->root->getChild('config'))
->setContent(file_get_contents($fileDir . 'B.config.php'));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('config'))
->setContent(file_get_contents($fileDir . 'B.ini.php'));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
->at($this->root->getChild('config'))
->setContent(file_get_contents($fileDir . 'B.ini.php'));
- $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+ $configFileLoader = new ConfigFileLoader($this->root->url());
$configCache = new ConfigCache();
$configFileLoader->setupCache($configCache);
namespace Friendica\Test\src\Util;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
use Friendica\Test\MockedTest;
use Friendica\Util\Profiler;
use Mockery\MockInterface;
*/
public function testSetUp()
{
- $profiler = new Profiler(true, true);
+ $configCache = \Mockery::mock(ConfigCache::class);
+ $configCache->shouldReceive('get')
+ ->withAnyArgs()
+ ->andReturn(true)
+ ->twice();
+ $profiler = new Profiler($configCache);
}
/**
*/
public function testSaveTimestamp($timestamp, $name, array $functions)
{
- $profiler = new Profiler(true, true);
+ $configCache = \Mockery::mock(ConfigCache::class);
+ $configCache->shouldReceive('get')
+ ->withAnyArgs()
+ ->andReturn(true)
+ ->twice();
+
+ $profiler = new Profiler($configCache);
foreach ($functions as $function) {
$profiler->saveTimestamp($timestamp, $name, $function);
*/
public function testReset($timestamp, $name, array $functions)
{
- $profiler = new Profiler(true, true);
+ $configCache = \Mockery::mock(ConfigCache::class);
+ $configCache->shouldReceive('get')
+ ->withAnyArgs()
+ ->andReturn(true)
+ ->twice();
+
+ $profiler = new Profiler($configCache);
$profiler->saveTimestamp($timestamp, $name);
$profiler->reset();
->shouldReceive('info')
->once();
- $profiler = new Profiler(true, true);
+ $configCache = \Mockery::mock(ConfigCache::class);
+ $configCache->shouldReceive('get')
+ ->withAnyArgs()
+ ->andReturn(true)
+ ->twice();
+
+ $profiler = new Profiler($configCache);
foreach ($data as $perf => $items) {
foreach ($items['functions'] as $function) {
*/
public function testEnableDisable()
{
- $profiler = new Profiler(true, false);
+ $configCache = \Mockery::mock(ConfigCache::class);
+ $configCache->shouldReceive('get')
+ ->with('system', 'profiler')
+ ->andReturn(true)
+ ->once();
+ $configCache->shouldReceive('get')
+ ->with('rendertime', 'callstack')
+ ->andReturn(false)
+ ->once();
+
+ $profiler = new Profiler($configCache);
$this->assertFalse($profiler->isRendertime());
$this->assertEmpty($profiler->getRendertimeString());
$profiler->saveTimestamp(time(), 'network', 'test1');
- $profiler->update(false, false);
+ $config = \Mockery::mock(Configuration::class);
+ $config->shouldReceive('get')
+ ->with('system', 'profiler')
+ ->andReturn(false)
+ ->once();
+ $config->shouldReceive('get')
+ ->with('rendertime', 'callstack')
+ ->andReturn(false)
+ ->once();
+
+ $profiler->update($config);
$this->assertFalse($profiler->isRendertime());
$this->assertEmpty($profiler->getRendertimeString());
- $profiler->update(true, true);
+ $config->shouldReceive('get')
+ ->with('system', 'profiler')
+ ->andReturn(true)
+ ->once();
+ $config->shouldReceive('get')
+ ->with('rendertime', 'callstack')
+ ->andReturn(true)
+ ->once();
+
+ $profiler->update($config);
$profiler->saveTimestamp(time(), 'database', 'test2');
--- /dev/null
+<div class="generic-page-wrapper">
+ <h1>{{$title}} <a href="help/Two-Factor-Authentication" title="{{$help_label}}" class="btn btn-default btn-sm"><i aria-hidden="true" class="fa fa-question fa-2x"></i></a></h1>
+ <div>{{$message nofilter}}</div>
+
+{{if $generated_app_specific_password}}
+ <div class="panel panel-success">
+ <div class="panel-heading">
+ ✅ {{$generated_app_specific_password.plaintext_password}}
+ </div>
+ <div class="panel-body">
+ {{$generated_message}}
+ </div>
+ </div>
+{{/if}}
+
+ <form action="settings/2fa/app_specific?t={{$password_security_token}}" method="post">
+ <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+ <table class="app-specific-passwords table table-hover table-condensed table-striped">
+ <thead>
+ <tr>
+ <th>{{$description_label}}</th>
+ <th>{{$last_used_label}}</th>
+ <th><button type="submit" name="action" class="btn btn-primary btn-small" value="revoke_all">{{$revoke_all_label}}</button></th>
+ </tr>
+ </thead>
+ <tbody>
+{{foreach $app_specific_passwords as $app_specific_password}}
+ <tr{{if $generated_app_specific_password && $app_specific_password.id == $generated_app_specific_password.id}} class="success"{{/if}}>
+ <td>
+ {{$app_specific_password.description}}
+ </td>
+ <td>
+ <span class="time" title="{{$app_specific_password.last_used}}" data-toggle="tooltip">
+ <time datetime="{{$app_specific_password.last_used}}">{{$app_specific_password.ago}}</time>
+ </span>
+ </td>
+ <td>
+ <button type="submit" name="revoke_id" class="btn btn-default btn-small" value="{{$app_specific_password.id}}">{{$revoke_label}}</button>
+ </td>
+ </tr>
+{{/foreach}}
+ </tbody>
+ </table>
+ </form>
+ <form action="settings/2fa/app_specific?t={{$password_security_token}}" method="post">
+ <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+ <h3>{{$generate_title}}</h3>
+ <p>{{$generate_message}}</p>
+ <div class="form-group">
+ <label for="app-specific-password-description">{{$description_label}}</label>
+ <input type="text" maxlength="255" name="description" id="app-specific-password-description" class="form-control" placeholder="{{$description_placeholder_label}}" required/>
+ </div>
+ <p>
+ <button type="submit" name="action" class="btn btn-large btn-primary" value="generate">{{$generate_label}}</button>
+ </p>
+ </form>
+</div>
{{include file="field_password.tpl" field=$password}}
- <div class="form-group settings-submit-wrapper" >
{{if !$has_secret}}
- <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="enable">{{$enable_label}}</button>
+ <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="enable">{{$enable_label}}</button></p>
{{else}}
- <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="disable">{{$disable_label}}</button>
+ <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="disable">{{$disable_label}}</button></p>
{{/if}}
{{if $has_secret && $verified}}
- <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recovery">{{$recovery_codes_label}}</button>
+ <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recovery">{{$recovery_codes_label}}</button></p>
+ <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="app_specific">{{$app_specific_passwords_label}}</button></p>
{{/if}}
{{if $has_secret && !$verified}}
- <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="configure">{{$configure_label}}</button>
+ <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="configure">{{$configure_label}}</button></p>
{{/if}}
- </div>
</form>
</div>
<div id="photo-tags">{{$tags.title}}
{{foreach $tags.tags as $t}}
<span class="category label btn-success sm">
- <span class="p-category">{{$t.name}}</span>
+ <span class="p-category">{{$t.name nofilter}}</span>
{{if $t.removeurl}} <a href="{{$t.removeurl}}">(X)</a> {{/if}}
</span>
{{/foreach}}