Compare commits

..

2 Commits

Author SHA1 Message Date
Sascha Nitsch a32220442c basic redis cache support 2024-07-17 21:45:33 +02:00
Sascha Nitsch d96b05a658 fixed errors found by phan 2024-07-17 21:37:02 +02:00
15 changed files with 159 additions and 29 deletions

View File

@ -359,6 +359,9 @@ return [
'directory_list' => [ 'directory_list' => [
'vendor/phan/phan/src/Phan', 'vendor/phan/phan/src/Phan',
'vendor/smarty/smarty/src', 'vendor/smarty/smarty/src',
'php/',
'plugins',
'htdocs',
], ],
// A list of individual files to include in analysis // A list of individual files to include in analysis

View File

@ -25,6 +25,12 @@ Needed SQL commands:
This will be changed, but works for the current develop verison. This will be changed, but works for the current develop verison.
If the include redis cache is enabled, create a users.acl for redis with the content:
user federator on ~u_* +get +set >redis*change*password
change password in the rediscache.ini to match your given password.
To configure an apache server, add the following rewrite rules: To configure an apache server, add the following rewrite rules:
<Directory /where/ever/you/put/it> <Directory /where/ever/you/put/it>

View File

@ -11,7 +11,7 @@ if (! array_key_exists('_call', $_REQUEST)) {
exit(); exit();
} }
date_default_timezone_set("Europe/Berlin"); date_default_timezone_set("Europe/Berlin");
spl_autoload_register(function ($className) { spl_autoload_register(static function (string $className) {
// strip Federator from class path // strip Federator from class path
$className = str_replace('Federator\\', '', $className); $className = str_replace('Federator\\', '', $className);
include '../php/' . str_replace("\\", "/", strtolower($className)) . '.php'; include '../php/' . str_replace("\\", "/", strtolower($className)) . '.php';

View File

@ -30,7 +30,7 @@ class Api extends Main
/** /**
* current user * current user
* *
* @var Data\User $user * @var Data\User|false $user
* */ * */
private $user; private $user;
@ -56,8 +56,9 @@ class Api extends Main
* *
* @param string $call * @param string $call
* path of called function * path of called function
* @return void
*/ */
public function setPath(string $call) : void public function setPath($call)
{ {
$this->path = $call; $this->path = $call;
while ($this->path[0] === '/') { while ($this->path[0] === '/') {
@ -71,7 +72,7 @@ class Api extends Main
*/ */
public function run() : void public function run() : void
{ {
$this->setPath($_REQUEST["_call"]); $this->setPath((string)$_REQUEST['_call']);
$this->openDatabase(); $this->openDatabase();
$this->loadPlugins(); $this->loadPlugins();
$retval = ""; $retval = "";
@ -131,6 +132,7 @@ class Api extends Main
if ($this->redirect !== null) { if ($this->redirect !== null) {
header("Location: $this->redirect"); header("Location: $this->redirect");
} }
// @phan-suppress-next-line PhanSuspiciousValueComparison
if ($this->responseCode != 200) { if ($this->responseCode != 200) {
http_response_code($this->responseCode); http_response_code($this->responseCode);
} }
@ -168,6 +170,9 @@ class Api extends Main
public function checkPermission($permission, $exception = "\Exceptions\PermissionDenied", $message = null) : void public function checkPermission($permission, $exception = "\Exceptions\PermissionDenied", $message = null) : void
{ {
// generic check first // generic check first
if ($this->user === false) {
throw new Exceptions\PermissionDenied();
}
if ($this->user->id == 0) { if ($this->user->id == 0) {
throw new Exceptions\PermissionDenied(); throw new Exceptions\PermissionDenied();
} }
@ -228,10 +233,7 @@ class Api extends Main
if ($int === true) { if ($int === true) {
return intval($_POST[$key]); return intval($_POST[$key]);
} }
$ret = $this->dbh->escape_string($this->stripHTML($_POST[$key])); $ret = $this->dbh->escape_string($this->stripHTML((string)$_POST[$key]));
if ($ret === false) {
return $int ? 0 : "";
}
return $ret; return $ret;
} }

View File

@ -30,7 +30,7 @@ class Dummy implements \Federator\Api\V1
/** /**
* constructor * constructor
* *
* @param \Main $main main instance * @param \Federator\Main $main main instance
*/ */
public function __construct(\Federator\Main $main) public function __construct(\Federator\Main $main)
{ {

3
php/cache/cache.php vendored
View File

@ -15,9 +15,10 @@ interface Cache extends \Federator\Connector\Connector
{ {
/** /**
* save remote user by given session * save remote user by given session
*
* @param string $_session session id * @param string $_session session id
* @param string $_user user/profile name * @param string $_user user/profile name
* @aramm \Federator\Data\User $user user data * @param \Federator\Data\User $user user data
* @return void * @return void
*/ */
public function saveRemoteUserBySession($_session, $_user, $user); public function saveRemoteUserBySession($_session, $_user, $user);

View File

@ -15,6 +15,7 @@ interface Connector
{ {
/** /**
* get remote user by given session * get remote user by given session
*
* @param string $_session session id * @param string $_session session id
* @param string $_user user/profile name * @param string $_user user/profile name
* @return \Federator\Data\User | false * @return \Federator\Data\User | false

View File

@ -38,6 +38,25 @@ class User
* */ * */
public $session; public $session;
/**
* create new user object from json string
*
* @param string $input input string
* @return User|false
*/
public static function createFromJson($input)
{
$data = json_decode($input, true);
if ($data === null) {
return false;
}
$user = new User();
$user->id = $data['id'];
/// TODO: replace with enums
$user->permissions = $data['permissions'];
return $user;
}
/** /**
* check if use has asked permission * check if use has asked permission
* @param string $p @unused-param * @param string $p @unused-param
@ -48,6 +67,20 @@ class User
*/ */
public function hasPermission(string $p) public function hasPermission(string $p)
{ {
return in_array($p, $this->permissions); return in_array($p, $this->permissions, false);
}
/**
* convert internal data to json string
*
* @return string
*/
public function toJson()
{
$data = [
'id' => $this->id,
'permissions' => $this->permissions
];
return json_encode($data) | '';
} }
} }

View File

@ -33,28 +33,32 @@ class User
* session * session
* @param string $_user * @param string $_user
* user/profile name * user/profile name
* @param \Connector\Connector? $connector @unused-param * @param \Federator\Connector\Connector $connector
* connector to fetch use with * connector to fetch use with
* @param \Cache\Cache? $cache * @param \Federator\Cache\Cache|null $cache
* optional caching service * optional caching service
* @return \Data\User|bool * @return \Federator\Data\User|false
*/ */
public static function getUserBySession($dbh, $_session, $_user, $connector, $cache) public static function getUserBySession($dbh, $_session, $_user, $connector, $cache)
{ {
$saveToCache = false;
$user = false; $user = false;
if ($cache) { if ($cache !== null) {
$user = $cache->getRemoteUserBySession($_session, $_user); $user = $cache->getRemoteUserBySession($_session, $_user);
} }
if ($user === false) {
// ask connector for user-id // ask connector for user-id
$user = $connector->getRemoteUserBySession($_session, $_user); $user = $connector->getRemoteUserBySession($_session, $_user);
$saveToCache = true;
}
if ($user === false) { if ($user === false) {
return false; return false;
} }
if ($cache) { self::extendUser($dbh, $user);
if ($cache !== null && $saveToCache) {
$cache->saveRemoteUserBySession($_session, $_user, $user); $cache->saveRemoteUserBySession($_session, $_user, $user);
} }
self::extendUser($dbh, $user);
return $user; return $user;
} }
} }

View File

@ -54,7 +54,7 @@ class Language
} }
} }
if ($uselang === null && array_key_exists('_lang', $_REQUEST)) { if ($uselang === null && array_key_exists('_lang', $_REQUEST)) {
$language = $_REQUEST['_lang']; $language = (string)$_REQUEST['_lang'];
if (array_key_exists($language, $this->validLanguages)) { if (array_key_exists($language, $this->validLanguages)) {
$uselang = $language; $uselang = $language;
} }
@ -126,7 +126,7 @@ class Language
* *
* @param string $group * @param string $group
* group name to fetch keys * group name to fetch keys
* @return array<string> list of keys * @return list<string> list of keys
*/ */
public function getKeys(string $group) public function getKeys(string $group)
{ {
@ -135,6 +135,7 @@ class Language
require_once($_SERVER['DOCUMENT_ROOT'] . '/../lang/' . $this->uselang . "/$group.inc"); require_once($_SERVER['DOCUMENT_ROOT'] . '/../lang/' . $this->uselang . "/$group.inc");
$this->lang[$group] = $l; $this->lang[$group] = $l;
} }
// @phan-suppress-next-line PhanPartialTypeMismatchReturn
return array_keys($this->lang[$group]); return array_keys($this->lang[$group]);
} }
@ -263,11 +264,11 @@ function smarty_function_printlang($params, $template) : string
{ {
$lang = $template->getTemplateVars("language"); $lang = $template->getTemplateVars("language");
<<<'PHAN' <<<'PHAN'
@phan-var \Language $lang @phan-var \Federator\Language $lang
PHAN; PHAN;
$forcelang = array_key_exists('lang', $params) ? $params['lang'] : null; $forcelang = array_key_exists('lang', $params) ? $params['lang'] : null;
if ($forcelang !== null) { if ($forcelang !== null) {
$lang = new Language($forcelang); $lang = new \Federator\Language($forcelang);
} }
if (isset($params['var'])) { if (isset($params['var'])) {
return $lang->printlang($params['group'], $params['key'], $params['var']); return $lang->printlang($params['group'], $params['key'], $params['var']);

View File

@ -53,7 +53,7 @@ class Main
/** /**
* languange instance * languange instance
* *
* @var \Language $lang * @var Language $lang
*/ */
protected $lang = null; protected $lang = null;
/** /**
@ -80,7 +80,11 @@ class Main
*/ */
public function __construct() public function __construct()
{ {
$this->config = parse_ini_file('../config.ini', true); $this->responseCode = 200;
$config = parse_ini_file('../config.ini', true);
if ($config !== false) {
$this->config = $config;
}
} }
/** /**
@ -92,6 +96,9 @@ class Main
public static function getFromRemote(string $remoteURL, $headers) public static function getFromRemote(string $remoteURL, $headers)
{ {
$ch = curl_init(); $ch = curl_init();
if ($ch === false) {
return ['', null];
}
curl_setopt($ch, CURLOPT_URL, $remoteURL); curl_setopt($ch, CURLOPT_URL, $remoteURL);
curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

View File

@ -23,7 +23,7 @@ class DummyConnector implements Connector
* get remote user by given session * get remote user by given session
* @param string $_session session id * @param string $_session session id
* @param string $_user user or profile name * @param string $_user user or profile name
* @return Data\User | false * @return \Federator\Data\User | false
*/ */
public function getRemoteUserBySession(string $_session, string $_user) public function getRemoteUserBySession(string $_session, string $_user)
{ {

View File

@ -13,6 +13,20 @@ namespace Federator\Cache;
*/ */
class RedisCache implements Cache class RedisCache implements Cache
{ {
/**
* config data
*
* @var array<string, mixed> $config
*/
private $config;
/**
* connection to redis open flag
*
* @var bool $connected
*/
private $connected = false;
/** /**
* connection handle * connection handle
* *
@ -25,20 +39,72 @@ class RedisCache implements Cache
*/ */
public function __construct() public function __construct()
{ {
$config = parse_ini_file('../rediscache.ini');
if ($config !== false) {
$this->config = $config;
}
} }
/** /**
* {@inheritDoc} * connect to redis
* @return void
*/
private function connect()
{
$this->redis = new \Redis();
$this->redis->pconnect($this->config['host'], intval($this->config['port'], 10));
// @phan-suppress-next-line PhanTypeMismatchArgumentInternalProbablyReal
$this->redis->auth([$this->config['username'], $this->config['password']]);
}
/**
* create key from session and user
*
* @param string $prefix prefix to create name spaces
* @param string $_session session id
* @param string $_user user/profile name
* @return string key
*/
private static function createKey($prefix, $_session, $_user)
{
$key = $prefix . '_';
$key .= md5($_session . $_user);
return $key;
}
/**
* get remote user by given session
*
* @param string $_session session id
* @param string $_user user/profile name
* @return \Federator\Data\User | false
*/ */
public function getRemoteUserBySession($_session, $_user) public function getRemoteUserBySession($_session, $_user)
{ {
if (!$this->connected) {
$this->connect();
}
$key = self::createKey('u', $_session, $_user);
$data = $this->redis->get($key);
if ($data === false) {
return false;
}
$user = \Federator\Data\User::createFromJson($data);
return $user;
} }
/** /**
* {@inheritDoc} * save remote user by given session
*
* @param string $_session session id
* @param string $_user user/profile name
* @param \Federator\Data\User $user user data
* @return void
*/ */
public function saveRemoteUserBySession($_session, $_user, $user) public function saveRemoteUserBySession($_session, $_user, $user)
{ {
$key = self::createKey('u', $_session, $_user);
$serialized = $user->toJson();
$this->redis->set($key, $serialized);
} }
} }

View File

@ -14,8 +14,9 @@ primary goal is to connect ContentNation via ActivityPub again.
## roadmap to v1.0 ## roadmap to v1.0
- [X] API framework - [X] API framework
- [X] interfact to connect to existing service - [X] interfact to connect to existing service
- [X] cache layer for users minmal version
- [ ] overlay to extend with needed info like private keys, urls, ... - [ ] overlay to extend with needed info like private keys, urls, ...
- [ ] cache layer for users - [ ] full cache for users
- [ ] webfinger - [ ] webfinger
- [ ] discovery endpoints - [ ] discovery endpoints
- [ ] ap outbox - [ ] ap outbox

5
rediscache.ini Normal file
View File

@ -0,0 +1,5 @@
[redis]
host = localhost
port = 6379
username = federator
password = redis*change*password