mirror of
https://github.com/Deutscher-Tischfussballbund/com_sportsmanager.git
synced 2026-06-10 06:27:52 +00:00
315 lines
9.0 KiB
PHP
315 lines
9.0 KiB
PHP
<?php
|
|
/*
|
|
* Sports Manager API Extension
|
|
*/
|
|
|
|
use JetBrains\PhpStorm\NoReturn;
|
|
use Joomla\CMS\Application\SiteApplication;
|
|
use Joomla\CMS\Factory;
|
|
use Joomla\CMS\User\UserFactoryInterface;
|
|
use Joomla\Registry\Registry;
|
|
|
|
defined("_JEXEC") or die();
|
|
|
|
require_once JPATH_SITE . '/components/com_sportsmanager/database/init.php';
|
|
|
|
Factory::getContainer()->set(Registry::class, function () {
|
|
return new Registry();
|
|
});
|
|
|
|
$secret = Factory::getContainer()->get(Registry::class)->get("secret");
|
|
|
|
#[NoReturn] function abortWithError($error): void
|
|
{
|
|
if (isJson()) {
|
|
header("content-type: application/json");
|
|
die(json_encode(["error" => $error]));
|
|
} else {
|
|
die($error);
|
|
}
|
|
}
|
|
|
|
function isJson(): bool
|
|
{
|
|
$jInput = Factory::getContainer()->get(SiteApplication::class)->input;
|
|
return $jInput->get('format') === 'json';
|
|
}
|
|
|
|
function notifyChange($data): void
|
|
{
|
|
|
|
try {
|
|
$db = getDatabase();
|
|
$query = "SELECT wert from #__sportsmanager_einstellungen WHERE name='api_push_key'";
|
|
$push_key = loadResult($db, $query);
|
|
$push_server = !empty($push_key) && isset(_payload($push_key)->aud) ? _payload($push_key)->aud : '';
|
|
if ($push_server != '' && $push_key != '') {
|
|
$url = $push_server . (str_ends_with($push_server, '/') ? '' : '/') . 'v1/notifications/send';
|
|
$key = 'key=' . $push_key;
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt_array($ch, array(
|
|
CURLOPT_POST => TRUE,
|
|
CURLOPT_RETURNTRANSFER => TRUE,
|
|
CURLOPT_HEADER => TRUE,
|
|
CURLOPT_HTTPHEADER => array(
|
|
'Authorization: ' . $key,
|
|
'Content-Type: application/json',
|
|
),
|
|
CURLOPT_TIMEOUT => 2,
|
|
CURLOPT_POSTFIELDS => json_encode($data),
|
|
));
|
|
$resp = curl_exec($ch);
|
|
if (!$resp) {
|
|
error_log("failed to send notification");
|
|
}
|
|
}
|
|
} catch (Exception $ex) {
|
|
error_log($ex);
|
|
}
|
|
}
|
|
|
|
function begegnungChanged($begegnung, $begegnung_vorher, $modus, $heim_team, $gast_team, $spiele): void
|
|
{
|
|
notifyChange(['payload' => [
|
|
'begegnung' => $begegnung,
|
|
'begegnung_vorher' => $begegnung_vorher,
|
|
'$modus' => $modus,
|
|
'heim_team' => $heim_team,
|
|
'gast_team' => $gast_team,
|
|
'spiele' => $spiele,
|
|
], 'type' => 'FIXTURE_RESULT_CHANGED']);
|
|
}
|
|
|
|
function begegnungTischChanged($begegnung, $heim_team, $gast_team): void
|
|
{
|
|
notifyChange(['payload' => [
|
|
'begegnung' => $begegnung,
|
|
'heim_team' => $heim_team,
|
|
'gast_team' => $gast_team,
|
|
], 'type' => 'TABLE_CHANGED']);
|
|
}
|
|
|
|
function begegnungVerlegenNotify($begegnung, $users, $vorschlagendes_team_id, $heim_team, $gast_team): void
|
|
{
|
|
notifyChange([
|
|
'payload' => [
|
|
'begegnung' => $begegnung,
|
|
'users' => $users,
|
|
'vorschlagendes_team_id' => $vorschlagendes_team_id,
|
|
'heim_team' => $heim_team,
|
|
'gast_team' => $gast_team,
|
|
],
|
|
'type' => 'FIXTURE_DATE_CHANGED'
|
|
]);
|
|
}
|
|
|
|
/*
|
|
* erstellen von request tokens. diese sind 16 stunden gültig.
|
|
* @reponse body
|
|
* { data: { token: "reqest_token", access_for_team: ["team_id_1", "team_id_2"]}, expires: 1520013747000}
|
|
*/
|
|
#[NoReturn] function userToken(): void
|
|
{
|
|
global $secret;
|
|
if (!isJson()) {
|
|
abortWithError("JSON Request only");
|
|
}
|
|
if (isExternalDatabase()) {
|
|
abortWithError("Local Database only");
|
|
}
|
|
$container = Factory::getContainer();
|
|
$jInput = $container->get(SiteApplication::class)->input->json;
|
|
$access_key = $jInput->getString('access_key');
|
|
|
|
$user_id = _payload($access_key)->sub;
|
|
$user = $container->get(UserFactoryInterface::class)->loadUserById($user_id);
|
|
|
|
if (!jwt_validate($access_key, $secret . $user->password)) {
|
|
abortWithError('Access Key is invalid');
|
|
}
|
|
|
|
try {
|
|
$expires = new DateTime();
|
|
$expires->modify('+16 hours');
|
|
$db = getDatabase();
|
|
$query = "SELECT berechtigt_team_id from #__sportsmanager_berechtigt_fuer_team where berechtigt_user_id = $user_id";
|
|
$team_id = loadObjectList($db, $query);
|
|
|
|
JSON_sportsmanager::JSON([
|
|
'token' => jwt_token([
|
|
'sub' => $user_id,
|
|
'exp' => $expires->getTimestamp(),
|
|
], $secret),
|
|
'access_for_teams' => array_map(function ($item) {
|
|
return $item->berechtigt_team_id;
|
|
}, $team_id),
|
|
'expires' => $expires->getTimestamp() * 1000, //
|
|
]);
|
|
} catch (Exception $ex) {
|
|
error_log($ex);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
*
|
|
* @response body
|
|
* { data: { token: "api_acccess_token" }}
|
|
*/
|
|
#[NoReturn] function userAuth(): void
|
|
{
|
|
global $secret;
|
|
if (!isJson()) {
|
|
die();
|
|
}
|
|
if (isExternalDatabase()) {
|
|
abortWithError("Local Database only");
|
|
}
|
|
$container = Factory::getContainer();
|
|
$jInput = $container->get(SiteApplication::class)->input->json;
|
|
$username = $jInput->getString('username');
|
|
$password = $jInput->getString('password');
|
|
|
|
$db = getDatabase();
|
|
$query = $db->getQuery(true);
|
|
$query->select('id')->from('#__users')->where('username = "' . $username . '"')->setLimit(1);
|
|
$user_id = loadResult($db, $query);
|
|
$user = $container->get(UserFactoryInterface::class)->loadUserById($user_id);
|
|
|
|
//TODO: pw verification modernising: use php native methods, however this also needs new pw hashing. maybe force a pw reset on all accounts
|
|
if (password_verify($password, $user->password)) {
|
|
|
|
JSON_sportsmanager::JSON([
|
|
'token' => jwt_token([
|
|
'sub' => $user_id,
|
|
'iat' => (new DateTime())->getTimestamp(),
|
|
], $secret . $user->password)
|
|
]);
|
|
}
|
|
abortWithError('Wrong credentials');
|
|
}
|
|
|
|
function getUserID(): int
|
|
{
|
|
global $secret;
|
|
$container = Factory::getContainer();
|
|
$input = $container->get(SiteApplication::class)->input;
|
|
$token = $input->server->getString('HTTP_SECRET', NULL);
|
|
|
|
return $token != NULL && jwt_validate($token, $secret) && isset(_payload($token)->sub)
|
|
? (int)_payload($token)->sub
|
|
: 0;
|
|
}
|
|
|
|
function getColorOfImage($image)
|
|
{
|
|
if ($image != NULL) {
|
|
|
|
if (str_contains($image, '.png')) {
|
|
$img = imagecreatefrompng($image);
|
|
} else {
|
|
$img = imagecreatefromjpeg($image);
|
|
}
|
|
$width = imagesx($img);
|
|
$height = imagesx($img);
|
|
$colorMap = [];
|
|
$colors = [];
|
|
for ($x = 0; $x < $width; $x++) {
|
|
for ($y = 0; $y < $height; $y++) {
|
|
$color = imagecolorsforindex($img, imagecolorat($img, $x, $y));
|
|
if ($color['alpha'] < 20) {
|
|
$c = colorKey($color);
|
|
$hex = hex($color);
|
|
if ($hex != NULL) {
|
|
if (!isset($colors[$c])) {
|
|
$colors[$c] = 0;
|
|
$colorMap[$c] = $hex;
|
|
}
|
|
$colors[$c] += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
arsort($colors);
|
|
$result = array_keys($colors);
|
|
return sizeof($result) > 1 && $result[0] === '0-0-0' ? $colorMap[$result[1]] : $colorMap[$result[0]];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
function colorKey($rgb): string
|
|
{
|
|
|
|
$r = (int)($rgb['red'] / 100);
|
|
$g = (int)($rgb['green'] / 100);
|
|
$b = (int)($rgb['blue'] / 100);
|
|
|
|
return $r . '-' . $g . '-' . $b;
|
|
}
|
|
|
|
function hex($rgb): ?string
|
|
{
|
|
$r = $rgb['red'];
|
|
$g = $rgb['green'];
|
|
$b = $rgb['blue'];
|
|
if (($r + $g + $b) / 3 > 125) {
|
|
return NULL;
|
|
}
|
|
$r = dechex($r);
|
|
if (strlen($r) < 2) {
|
|
$r = '0' . $r;
|
|
}
|
|
$g = dechex($g);
|
|
if (strlen($g) < 2) {
|
|
$g = '0' . $g;
|
|
}
|
|
$b = dechex($b);
|
|
if (strlen($b) < 2) {
|
|
$b = '0' . $b;
|
|
}
|
|
return '#' . $r . $g . $b;
|
|
}
|
|
|
|
/*
|
|
* sign string with secret
|
|
*/
|
|
function _sign($data, $secret): string
|
|
{
|
|
return base64_encode(hash_hmac('sha256', $data, $secret));
|
|
}
|
|
|
|
/*
|
|
* get payload from jwt token
|
|
*/
|
|
function _payload($token)
|
|
{
|
|
$jwt = explode('.', $token);
|
|
return json_decode(base64_decode($jwt[0]));
|
|
}
|
|
|
|
/*
|
|
* headless signed jwt token
|
|
*/
|
|
function jwt_token($payload, $secret): string
|
|
{
|
|
$data = base64_encode(json_encode($payload));
|
|
|
|
return $data . '.' . _sign($data, $secret);
|
|
}
|
|
|
|
/*
|
|
* validate token
|
|
*/
|
|
function jwt_validate($token, $secret): bool
|
|
{
|
|
$jwt = explode('.', $token);
|
|
if (sizeof($jwt) == 2 && $jwt[1] == _sign($jwt[0], $secret)) {
|
|
if (isset(_payload($token)->exp)) {
|
|
return _payload($token)->exp > (new DateTime())->getTimestamp();
|
|
}
|
|
return true;
|
|
}
|
|
return sizeof($jwt) == 2 && $jwt[1] == _sign($jwt[0], $secret);
|
|
}
|