initial webfinger support
parent
eed9678dbf
commit
47efd74b6c
|
@ -39,6 +39,7 @@ To configure an apache server, add the following rewrite rules:
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
RewriteBase /
|
RewriteBase /
|
||||||
RewriteRule ^api/(.+)$ api.php?_call=$1 [L]
|
RewriteRule ^api/(.+)$ api.php?_call=$1 [L]
|
||||||
|
RewriteRule ^(\.well-known/.*)$ /api.php?_call=$1 [L,END]
|
||||||
</Directory>
|
</Directory>
|
||||||
|
|
||||||
With the dummy plugin and everything installed correctly a
|
With the dummy plugin and everything installed correctly a
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
[generic]
|
||||||
|
externaldomain = 'your.fqdn'
|
||||||
|
|
||||||
[database]
|
[database]
|
||||||
host = '127.0.0.1'
|
host = '127.0.0.1'
|
||||||
username = 'federator'
|
username = 'federator'
|
||||||
|
@ -5,7 +8,7 @@ password = '*change*me*'
|
||||||
database = 'federator'
|
database = 'federator'
|
||||||
|
|
||||||
[templates]
|
[templates]
|
||||||
path = '../templates/'
|
path = '../templates/federator/'
|
||||||
compiledir = '../cache'
|
compiledir = '../cache'
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
|
|
|
@ -46,7 +46,6 @@ class Api extends Main
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->smarty = null;
|
|
||||||
$this->contentType = "application/json";
|
$this->contentType = "application/json";
|
||||||
Main::__construct();
|
Main::__construct();
|
||||||
}
|
}
|
||||||
|
@ -77,40 +76,39 @@ class Api extends Main
|
||||||
$this->loadPlugins();
|
$this->loadPlugins();
|
||||||
$retval = "";
|
$retval = "";
|
||||||
$handler = null;
|
$handler = null;
|
||||||
if (!array_key_exists('HTTP_X_SESSION', $_SERVER) || !array_key_exists('HTTP_X_PROFILE', $_SERVER)) {
|
|
||||||
http_response_code(403);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ($this->connector === null) {
|
if ($this->connector === null) {
|
||||||
http_response_code(500);
|
http_response_code(500);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->user = DIO\User::getUserBySession(
|
if (array_key_exists('HTTP_X_SESSION', $_SERVER) && array_key_exists('HTTP_X_PROFILE', $_SERVER)) {
|
||||||
$this->dbh,
|
$this->user = DIO\User::getUserBySession(
|
||||||
$_SERVER['HTTP_X_SESSION'],
|
$this->dbh,
|
||||||
$_SERVER['HTTP_X_PROFILE'],
|
$_SERVER['HTTP_X_SESSION'],
|
||||||
$this->connector,
|
$_SERVER['HTTP_X_PROFILE'],
|
||||||
$this->cache
|
$this->connector,
|
||||||
);
|
$this->cache
|
||||||
if ($this->user === false) {
|
);
|
||||||
http_response_code(403);
|
if ($this->user === false) {
|
||||||
return;
|
http_response_code(403);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
switch ($this->path[0]) {
|
switch ($this->paths[0]) {
|
||||||
case 'v':
|
case 'v1':
|
||||||
if ($this->paths[0] === "v1") {
|
switch ($this->paths[1]) {
|
||||||
switch ($this->paths[1]) {
|
case 'dummy':
|
||||||
case 'dummy':
|
$handler = new Api\V1\Dummy($this);
|
||||||
$handler = new Api\V1\Dummy($this);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case '.well-known':
|
||||||
|
$handler = new Api\WellKnown($this);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$printresponse = true;
|
$printresponse = true;
|
||||||
if ($handler !== null) {
|
if ($handler !== null) {
|
||||||
try {
|
try {
|
||||||
$printresponse = $handler->exec($this->paths);
|
$printresponse = $handler->exec($this->paths, $this->user);
|
||||||
if ($printresponse) {
|
if ($printresponse) {
|
||||||
$retval = $handler->toJson();
|
$retval = $handler->toJson();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,17 @@ namespace Federator\Api;
|
||||||
/**
|
/**
|
||||||
* API interface
|
* API interface
|
||||||
*/
|
*/
|
||||||
interface V1
|
interface APIInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* run given url path
|
* run given url path
|
||||||
*
|
*
|
||||||
* @param array<string> $paths path array split by /
|
* @param array<string> $paths path array split by /
|
||||||
|
*
|
||||||
|
* @param \Federator\Data\User|false $user user who is calling us
|
||||||
* @return bool true on success
|
* @return bool true on success
|
||||||
*/
|
*/
|
||||||
public function exec($paths);
|
public function exec($paths, $user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get internal represenation as json string
|
* get internal represenation as json string
|
|
@ -11,7 +11,7 @@ namespace Federator\Api\V1;
|
||||||
/**
|
/**
|
||||||
* dummy api class for functional poc
|
* dummy api class for functional poc
|
||||||
*/
|
*/
|
||||||
class Dummy implements \Federator\Api\V1
|
class Dummy implements \Federator\Api\APIInterface
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* \Federator\Main instance
|
* \Federator\Main instance
|
||||||
|
@ -41,10 +41,15 @@ class Dummy implements \Federator\Api\V1
|
||||||
* run given url path
|
* run given url path
|
||||||
*
|
*
|
||||||
* @param array<string> $paths path array split by /
|
* @param array<string> $paths path array split by /
|
||||||
|
* @param \Federator\Data\User|false $user user who is calling us
|
||||||
* @return bool true on success
|
* @return bool true on success
|
||||||
*/
|
*/
|
||||||
public function exec($paths) : bool
|
public function exec($paths, $user) : bool
|
||||||
{
|
{
|
||||||
|
// only for user with the 'publish' permission
|
||||||
|
if ($user === false || $user->hasPermission('publish') === false) {
|
||||||
|
throw new \Federator\Exceptions\PermissionDenied();
|
||||||
|
}
|
||||||
$method = $_SERVER["REQUEST_METHOD"];
|
$method = $_SERVER["REQUEST_METHOD"];
|
||||||
switch ($method) {
|
switch ($method) {
|
||||||
case 'GET':
|
case 'GET':
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\Api;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* .well-known handlers
|
||||||
|
*/
|
||||||
|
class WellKnown implements APIInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* main instance
|
||||||
|
*
|
||||||
|
* @var \Federator\Main $main
|
||||||
|
*/
|
||||||
|
private $main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* response from sub-calls
|
||||||
|
*
|
||||||
|
* @var string $response
|
||||||
|
*/
|
||||||
|
private $response;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @param \Federator\Main $main main instance
|
||||||
|
*/
|
||||||
|
public function __construct($main)
|
||||||
|
{
|
||||||
|
$this->main = $main;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* run given url path
|
||||||
|
*
|
||||||
|
* @param array<string> $paths path array split by /
|
||||||
|
* @param \Federator\Data\User|false $user user who is calling us @unused-param
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
public function exec($paths, $user)
|
||||||
|
{
|
||||||
|
$method = $_SERVER["REQUEST_METHOD"];
|
||||||
|
switch ($method) {
|
||||||
|
case 'GET':
|
||||||
|
switch (sizeof($paths)) {
|
||||||
|
case 2:
|
||||||
|
if ($paths[1] === 'webfinger') {
|
||||||
|
$wf = new WellKnown\WebFinger($this, $this->main);
|
||||||
|
return $wf->exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$this->main->setResponseCode(404);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set response
|
||||||
|
*
|
||||||
|
* @param string $response response to set
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setResponse($response)
|
||||||
|
{
|
||||||
|
$this->response = $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get internal represenation as json string
|
||||||
|
* @return string json string or html
|
||||||
|
*/
|
||||||
|
public function toJson()
|
||||||
|
{
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\Api\WellKnown;
|
||||||
|
|
||||||
|
class WebFinger
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* parent instance
|
||||||
|
*
|
||||||
|
* @var \Federator\Api\WellKnown $wellKnown
|
||||||
|
*/
|
||||||
|
private $wellKnown;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* main instance
|
||||||
|
*
|
||||||
|
* @var \Federator\Main $main
|
||||||
|
*/
|
||||||
|
private $main;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @param \Federator\Api\WellKnown $wellKnown parent instance
|
||||||
|
* @param \Federator\Main $main main instance
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct($wellKnown, $main)
|
||||||
|
{
|
||||||
|
$this->wellKnown = $wellKnown;
|
||||||
|
$this->main = $main;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle webfinger request
|
||||||
|
*
|
||||||
|
* @return bool true on success
|
||||||
|
*/
|
||||||
|
public function exec()
|
||||||
|
{
|
||||||
|
$_resource = $this->main->extractFromURI('resource');
|
||||||
|
$matches = [];
|
||||||
|
$config = $this->main->getConfig();
|
||||||
|
$domain = $config['generic']['externaldomain'];
|
||||||
|
if (preg_match("/^acct:([^@]+)@(.*)$/", $_resource, $matches) != 1 || $matches[2] !== $domain) {
|
||||||
|
throw new \Federator\Exceptions\InvalidArgument();
|
||||||
|
}
|
||||||
|
$user = \Federator\DIO\User::getUserByName($this->main->getDatabase(), $matches[1]);
|
||||||
|
if ($user->id == 0) {
|
||||||
|
throw new \Federator\Exceptions\FileNotFound();
|
||||||
|
}
|
||||||
|
$data = [
|
||||||
|
'username' => $user->id,
|
||||||
|
'domain' => $domain,
|
||||||
|
];
|
||||||
|
$response = $this->main->renderTemplate('webfinger_acct.json', $data);
|
||||||
|
$this->wellKnown->setResponse($response);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -70,6 +70,33 @@ class User
|
||||||
// no further processing for now
|
// no further processing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get user by name
|
||||||
|
*
|
||||||
|
* @param \mysqli $dbh
|
||||||
|
* database handle
|
||||||
|
* @param string $_name
|
||||||
|
* user name
|
||||||
|
* @return \Federator\Data\User
|
||||||
|
*/
|
||||||
|
public static function getUserByName($dbh, $_name)
|
||||||
|
{
|
||||||
|
$sql = 'select id from users where id=?';
|
||||||
|
$stmt = $dbh->prepare($sql);
|
||||||
|
if ($stmt === false) {
|
||||||
|
throw new \Federator\Exceptions\ServerError();
|
||||||
|
}
|
||||||
|
$stmt->bind_param("s", $_name);
|
||||||
|
$user = new \Federator\Data\User();
|
||||||
|
$ret = $stmt->bind_result($user->id);
|
||||||
|
$stmt->execute();
|
||||||
|
if ($ret) {
|
||||||
|
$stmt->fetch();
|
||||||
|
}
|
||||||
|
$stmt->close();
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get User by session id
|
* get User by session id
|
||||||
*
|
*
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
* @author Author: Sascha Nitsch (grumpydeveloper)
|
* @author Author: Sascha Nitsch (grumpydeveloper)
|
||||||
**/
|
**/
|
||||||
|
|
||||||
namespace Federator;
|
namespace Federator;
|
||||||
|
|
||||||
|
require_once($_SERVER['DOCUMENT_ROOT'] . '../vendor/autoload.php');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for Api and related classes
|
* Base class for Api and related classes
|
||||||
|
@ -51,30 +53,20 @@ class Main
|
||||||
* @var array<string,string> $headers
|
* @var array<string,string> $headers
|
||||||
*/
|
*/
|
||||||
protected $headers = [];
|
protected $headers = [];
|
||||||
/**
|
|
||||||
* languange instance
|
|
||||||
*
|
|
||||||
* @var Language $lang
|
|
||||||
*/
|
|
||||||
protected $lang = null;
|
|
||||||
/**
|
/**
|
||||||
* redirect URL
|
* redirect URL
|
||||||
*
|
*
|
||||||
* @var ?string $redirect
|
* @var ?string $redirect
|
||||||
*/
|
*/
|
||||||
protected $redirect = null;
|
protected $redirect = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* response code
|
* response code
|
||||||
*
|
*
|
||||||
* @var int $responseCode
|
* @var int $responseCode
|
||||||
*/
|
*/
|
||||||
protected $responseCode = 200;
|
protected $responseCode = 200;
|
||||||
/**
|
|
||||||
* smarty instance
|
|
||||||
*
|
|
||||||
* @var \Smarty\Smarty|null $smarty
|
|
||||||
*/
|
|
||||||
protected $smarty;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constructor
|
* constructor
|
||||||
|
@ -90,6 +82,29 @@ class Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* extract parameter from URI
|
||||||
|
*
|
||||||
|
* @param string $param
|
||||||
|
* parameter to extract
|
||||||
|
* @param string $fallback
|
||||||
|
* optional fallback
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function extractFromURI($param, $fallback = '')
|
||||||
|
{
|
||||||
|
$uri = $_SERVER['REQUEST_URI'];
|
||||||
|
$params = substr($uri, (int)(strpos($uri, '?') + 1));
|
||||||
|
$params = explode('&', $params);
|
||||||
|
foreach ($params as $p) {
|
||||||
|
$tokens = explode('=', $p);
|
||||||
|
if ($tokens[0] === $param) {
|
||||||
|
return urldecode($tokens[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do a remote call and return results
|
* do a remote call and return results
|
||||||
* @param string $remoteURL remote URL
|
* @param string $remoteURL remote URL
|
||||||
|
@ -128,7 +143,7 @@ class Main
|
||||||
/**
|
/**
|
||||||
* get database handle
|
* get database handle
|
||||||
*
|
*
|
||||||
* @return \mysqli|false database handle
|
* @return \mysqli database handle
|
||||||
*/
|
*/
|
||||||
public function getDatabase()
|
public function getDatabase()
|
||||||
{
|
{
|
||||||
|
@ -175,6 +190,27 @@ class Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render template
|
||||||
|
*
|
||||||
|
* @param string $template template file to render
|
||||||
|
* @param array<string, mixed> $data template variables
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function renderTemplate($template, $data)
|
||||||
|
{
|
||||||
|
$smarty = new \Smarty\Smarty();
|
||||||
|
$root = $_SERVER['DOCUMENT_ROOT'];
|
||||||
|
$smarty->setCompileDir($root . $this->config['templates']['compiledir']);
|
||||||
|
$smarty->setTemplateDir((string)realpath($root . $this->config['templates']['path']));
|
||||||
|
$smarty->assign('database', $this->dbh);
|
||||||
|
$smarty->assign('maininstance', $this);
|
||||||
|
foreach ($data as $key => $value) {
|
||||||
|
$smarty->assign($key, $value);
|
||||||
|
}
|
||||||
|
return $smarty->fetch($template);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set cache
|
* set cache
|
||||||
*/
|
*/
|
||||||
|
@ -215,20 +251,10 @@ class Main
|
||||||
* optional parameters
|
* optional parameters
|
||||||
* @return string translation
|
* @return string translation
|
||||||
*/
|
*/
|
||||||
public function translate(?string $lang, string $group, string $key, array $parameters = array()) : string
|
public static function translate(?string $lang, string $group, string $key, array $parameters = array()) : string
|
||||||
{
|
{
|
||||||
if ($this->lang === null) {
|
$l = new Language($lang);
|
||||||
$this->validLanguage($lang);
|
return $l->printlang($group, $key, $parameters);
|
||||||
}
|
|
||||||
if ($this->lang !== null) {
|
|
||||||
if ($this->lang->getLang() !== $lang) {
|
|
||||||
$l = new Language($lang);
|
|
||||||
return $l->printlang($group, $key, $parameters);
|
|
||||||
}
|
|
||||||
return $this->lang->printlang($group, $key, $parameters);
|
|
||||||
} else {
|
|
||||||
return $key;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -236,11 +262,10 @@ class Main
|
||||||
*
|
*
|
||||||
* @param ?string $lang
|
* @param ?string $lang
|
||||||
*/
|
*/
|
||||||
public function validLanguage(?string $lang) : bool
|
public static function validLanguage(?string $lang) : bool
|
||||||
{
|
{
|
||||||
$language = new Language($lang);
|
$language = new Language($lang);
|
||||||
if ($language->getLang() === $lang) {
|
if ($language->getLang() === $lang) {
|
||||||
$this->lang = $language;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*
|
||||||
|
* @author Sascha Nitsch (grumpydeveloper)
|
||||||
|
**/
|
||||||
|
|
||||||
|
namespace Federator\Connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connector to ContentNation.net
|
||||||
|
*/
|
||||||
|
class ContentNation implements Connector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* service-URL
|
||||||
|
*
|
||||||
|
* @var string $service
|
||||||
|
*/
|
||||||
|
private $service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* constructor
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $config
|
||||||
|
*/
|
||||||
|
public function __construct($config)
|
||||||
|
{
|
||||||
|
$this->service = $config['service-uri'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get remote user by given session
|
||||||
|
*
|
||||||
|
* @param string $_session session id
|
||||||
|
* @param string $_user user or profile name
|
||||||
|
* @return \Federator\Data\User | false
|
||||||
|
*/
|
||||||
|
public function getRemoteUserBySession(string $_session, string $_user)
|
||||||
|
{
|
||||||
|
// validate $_session and $user
|
||||||
|
if (preg_match("/^[a-z0-9]{16}$/", $_session) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (preg_match("/^[a-zA-Z0-9_\-]+$/", $_user) != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$remoteURL = $this->service . '/api/users/permissions?profile=' . urlencode($_user);
|
||||||
|
$headers = ['Cookie: session=' . $_session, 'Accept: application/json'];
|
||||||
|
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
|
||||||
|
|
||||||
|
if ($info['http_code'] != 200) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$r = json_decode($response, true);
|
||||||
|
if ($r === false || !is_array($r) || !array_key_exists($_user, $r)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$user = new \Federator\Data\User();
|
||||||
|
$user->externalid = $_user;
|
||||||
|
$user->permissions = [];
|
||||||
|
$user->session = $_session;
|
||||||
|
foreach ($r[$_user] as $p) {
|
||||||
|
$user->permissions[] = $p;
|
||||||
|
}
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Federator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function to initialize plugin
|
||||||
|
*
|
||||||
|
* @param \Federator\Main $main main instance
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function contentnation_load($main)
|
||||||
|
{
|
||||||
|
$cn = new Connector\ContentNation($main->getConfig()['contentnation']);
|
||||||
|
$main->setConnector($cn);
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ primary goal is to connect ContentNation via ActivityPub again.
|
||||||
- [X] cache layer for users minmal version
|
- [X] cache layer for users minmal version
|
||||||
- [X] overlay to extend with needed info like private keys, urls, ...
|
- [X] overlay to extend with needed info like private keys, urls, ...
|
||||||
- [X] full cache for users
|
- [X] full cache for users
|
||||||
- [ ] webfinger
|
- [X] webfinger
|
||||||
- [ ] discovery endpoints
|
- [ ] discovery endpoints
|
||||||
- [ ] ap outbox
|
- [ ] ap outbox
|
||||||
- [ ] ap inbox
|
- [ ] ap inbox
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{ldelim}
|
||||||
|
"subject": "acct:{$username}@{$domain}",
|
||||||
|
"aliases": ["https://{$domain}/@{$username}"],
|
||||||
|
"links": [
|
||||||
|
{ldelim}"rel": "self", "type": "application/activity+json", "href": "https://{$domain}/{$username}"{rdelim},
|
||||||
|
{if $type=='Group'}
|
||||||
|
{ldelim}"rel": "http://webfinger.net/rel/profile-page", "type": "text/html", "href": "https://{$domain}/@{$username}/"{rdelim},
|
||||||
|
{/if}
|
||||||
|
{ldelim}"rel": "http://ostatus.org/schema/1.0/subscribe", "template": "https://{$domain}/authorize_interaction?uri={ldelim}uri{rdelim}"{rdelim}
|
||||||
|
]
|
||||||
|
{rdelim}
|
Loading…
Reference in New Issue