Skip to content
Snippets Groups Projects
Verified Commit 44e507ae authored by Dominik František Bučík's avatar Dominik František Bučík
Browse files

feat: :guitar: API write

Ability to write into statistics by calling API endpoint

BREAKING CHANGE: Requires at least PHP v7.3 or higher (due to usage of
JSON_THROW_ON_ERROR constant)
parent 5d6cda42
No related branches found
No related tags found
1 merge request!80Feat: API write
...@@ -73,3 +73,26 @@ Once you have installed SimpleSAMLphp, installing this module is very simple. Fi ...@@ -73,3 +73,26 @@ Once you have installed SimpleSAMLphp, installing this module is very simple. Fi
``` ```
'instance_name' => 'Instance name', 'instance_name' => 'Instance name',
``` ```
### Writing via API
#### Configuration
Add the following (and adjust the credentials) to enable writing via the API (example request following). Methods supported are `POST,PUT`.
```
'apiWriteEnabled' => true,
'apiWriteUsername' => 'api_writer',
'apiWritePasswordHash' => password_hash('ap1Wr1T3rP@S$'),
```
#### Example request
```
curl --request POST \
--url https://proxy.com/proxy/module.php/proxystatistics/writeLoginApi.php \
--header 'Authorization: Basic encodedCredentials' \
--header 'Content-Type: application/json' \
--data '{
"userId": "user@somewhere.edu",
"serviceIdentifier": "https://service.com/shibboleth",
"serviceName": "TEST_SERVICE",
"idpIdentifier": "https://idp.org/simplesamlphp",
"idpName": "TEST_IDP"
}'
```
\ No newline at end of file
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
} }
}, },
"require": { "require": {
"php": "^7.1 || ^8", "php": "^7.3 || ^8",
"ext-ctype": "*", "ext-ctype": "*",
"ext-filter": "*", "ext-filter": "*",
"ext-json": "*", "ext-json": "*",
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "c6cf0239d2544ae5fb18d3e67487d9e8", "content-hash": "bf645ff7f6627d746e1d24538affe30a",
"packages": [ "packages": [
{ {
"name": "gettext/gettext", "name": "gettext/gettext",
...@@ -5064,7 +5064,7 @@ ...@@ -5064,7 +5064,7 @@
"prefer-stable": false, "prefer-stable": false,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^7.1 || ^8", "php": "^7.3 || ^8",
"ext-ctype": "*", "ext-ctype": "*",
"ext-filter": "*", "ext-filter": "*",
"ext-json": "*", "ext-json": "*",
......
...@@ -87,4 +87,19 @@ $config = [ ...@@ -87,4 +87,19 @@ $config = [
* For how many days should the detailed statistics be kept. Minimum is 31. * For how many days should the detailed statistics be kept. Minimum is 31.
*/ */
//'keepPerUser' => 62, //'keepPerUser' => 62,
/*
* Enables ability to write via an API
*/
//'apiWriteEnabled' => true,
/*
* Username to protect API write endpoint (has no effect if write is disabled)
*/
//'apiWriteUsername' => 'api_writer',
/*
* Password to protect API write endpoint (has no effect if write is disabled)
*/
//'apiWritePasswordHash' => password_hash('ap1Wr1T3rP@S$'),
]; ];
...@@ -26,7 +26,7 @@ class Statistics extends ProcessingFilter ...@@ -26,7 +26,7 @@ class Statistics extends ProcessingFilter
$dateTime = new DateTime(); $dateTime = new DateTime();
$dbCmd = new DatabaseCommand(); $dbCmd = new DatabaseCommand();
try { try {
$dbCmd->insertLogin($request, $dateTime); $dbCmd->insertLoginFromFilter($request, $dateTime);
} catch (Exception $ex) { } catch (Exception $ex) {
Logger::error( Logger::error(
self::DEBUG_PREFIX . 'Caught exception while inserting login into statistics: ' . $ex->getMessage() self::DEBUG_PREFIX . 'Caught exception while inserting login into statistics: ' . $ex->getMessage()
......
...@@ -5,6 +5,7 @@ declare(strict_types=1); ...@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace SimpleSAML\Module\proxystatistics; namespace SimpleSAML\Module\proxystatistics;
use SimpleSAML\Configuration; use SimpleSAML\Configuration;
use SimpleSAML\Error\Exception;
class Config class Config
{ {
...@@ -34,6 +35,12 @@ class Config ...@@ -34,6 +35,12 @@ class Config
private const KEEP_PER_USER = 'keepPerUser'; private const KEEP_PER_USER = 'keepPerUser';
private const API_WRITE_ENABLED = 'apiWriteEnabled';
private const API_WRITE_USERNAME = 'apiWriteUsername';
private const API_WRITE_PASSWORD_HASH = 'apiWritePasswordHash';
private $config; private $config;
private $store; private $store;
...@@ -50,6 +57,12 @@ class Config ...@@ -50,6 +57,12 @@ class Config
private $idAttribute; private $idAttribute;
private $apiWriteEnabled;
private $apiWriteUsername;
private $apiWritePasswordHash;
private static $instance; private static $instance;
private function __construct() private function __construct()
...@@ -62,6 +75,17 @@ class Config ...@@ -62,6 +75,17 @@ class Config
$this->keepPerUser = $this->config->getIntegerRange(self::KEEP_PER_USER, 31, 1827, 31); $this->keepPerUser = $this->config->getIntegerRange(self::KEEP_PER_USER, 31, 1827, 31);
$this->requiredAuthSource = $this->config->getString(self::REQUIRE_AUTH_SOURCE, ''); $this->requiredAuthSource = $this->config->getString(self::REQUIRE_AUTH_SOURCE, '');
$this->idAttribute = $this->config->getString(self::USER_ID_ATTRIBUTE, 'uid'); $this->idAttribute = $this->config->getString(self::USER_ID_ATTRIBUTE, 'uid');
$this->apiWriteEnabled = $this->config->getBoolean(self::API_WRITE_ENABLED, false);
if ($this->apiWriteEnabled) {
$this->apiWriteUsername = $this->config->getString(self::API_WRITE_USERNAME);
if (empty(trim($this->apiWriteUsername))) {
throw new Exception('Username for API write cannot be empty');
}
$this->apiWritePasswordHash = $this->config->getString(self::API_WRITE_PASSWORD_HASH);
if (empty(trim($this->apiWritePasswordHash))) {
throw new Exception('Password for API write cannot be empty');
}
}
} }
private function __clone() private function __clone()
...@@ -123,4 +147,19 @@ class Config ...@@ -123,4 +147,19 @@ class Config
{ {
return $this->keepPerUser; return $this->keepPerUser;
} }
public function isApiWriteEnabled()
{
return $this->apiWriteEnabled;
}
public function getApiWriteUsername()
{
return $this->apiWriteUsername;
}
public function getApiWritePasswordHash()
{
return $this->apiWritePasswordHash;
}
} }
...@@ -4,6 +4,7 @@ declare(strict_types=1); ...@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace SimpleSAML\Module\proxystatistics; namespace SimpleSAML\Module\proxystatistics;
use DateTime;
use Exception; use Exception;
use PDO; use PDO;
use PDOStatement; use PDOStatement;
...@@ -14,6 +15,16 @@ class DatabaseCommand ...@@ -14,6 +15,16 @@ class DatabaseCommand
{ {
public const TABLE_SUM = 'statistics_sums'; public const TABLE_SUM = 'statistics_sums';
public const API_USER_ID = 'userId';
public const API_SERVICE_NAME = 'serviceName';
public const API_SERVICE_IDENTIFIER = 'serviceIdentifier';
public const API_IDP_NAME = 'idpName';
public const API_IDP_IDENTIFIER = 'idpIdentifier';
private const DEBUG_PREFIX = 'proxystatistics:DatabaseCommand - '; private const DEBUG_PREFIX = 'proxystatistics:DatabaseCommand - ';
private const TABLE_PER_USER = 'statistics_per_user'; private const TABLE_PER_USER = 'statistics_per_user';
...@@ -66,21 +77,11 @@ class DatabaseCommand ...@@ -66,21 +77,11 @@ class DatabaseCommand
$this->mode = $this->config->getMode(); $this->mode = $this->config->getMode();
} }
public function insertLogin($request, &$date) public function insertLoginFromFilter($request, $date)
{ {
$entities = $this->prepareEntitiesData($request); $entities = $this->getEntities($request);
foreach (Config::SIDES as $side) {
if (empty($entities[$side][self::KEY_ID])) {
Logger::error(
self::DEBUG_PREFIX . 'idpEntityId or spEntityId is empty and login log was not inserted into the database.'
);
return;
}
}
$userId = $this->getUserId($request); $userId = $this->getUserId($request);
$this->insertLogin($entities, $userId, $date);
$ids = []; $ids = [];
foreach (self::TABLE_SIDES as $side => $table) { foreach (self::TABLE_SIDES as $side => $table) {
...@@ -88,7 +89,7 @@ class DatabaseCommand ...@@ -88,7 +89,7 @@ class DatabaseCommand
$ids[$tableId] = $this->getEntityDbIdFromEntityIdentifier($table, $entities[$side], $tableId); $ids[$tableId] = $this->getEntityDbIdFromEntityIdentifier($table, $entities[$side], $tableId);
} }
if ($this->writeLogin($date, $ids, $userId) === false) { if (!$this->writeLogin($date, $ids, $userId)) {
Logger::error(self::DEBUG_PREFIX . 'login record has not been inserted (data \'' . json_encode([ Logger::error(self::DEBUG_PREFIX . 'login record has not been inserted (data \'' . json_encode([
'user' => $userId, 'user' => $userId,
'ids' => $ids, 'ids' => $ids,
...@@ -97,6 +98,18 @@ class DatabaseCommand ...@@ -97,6 +98,18 @@ class DatabaseCommand
} }
} }
public function insertLoginFromApi($data, DateTime $date)
{
$userId = $data[self::API_USER_ID];
$serviceIdentifier = $data[self::API_SERVICE_IDENTIFIER];
$serviceName = $data[self::API_SERVICE_NAME];
$idpIdentifier = $data[self::API_IDP_IDENTIFIER];
$idpName = $data[self::API_IDP_NAME];
$entities = $this->prepareEntitiesStructure($idpIdentifier, $idpName, $serviceIdentifier, $serviceName);
$this->insertLogin($entities, $userId, $date);
}
public function getEntityNameByEntityIdentifier($side, $id) public function getEntityNameByEntityIdentifier($side, $id)
{ {
$table = self::TABLE_SIDES[$side]; $table = self::TABLE_SIDES[$side];
...@@ -165,21 +178,21 @@ class DatabaseCommand ...@@ -165,21 +178,21 @@ class DatabaseCommand
$msg = 'Aggregating daily statistics per ' . implode(' and ', array_filter($ids)); $msg = 'Aggregating daily statistics per ' . implode(' and ', array_filter($ids));
Logger::info(self::DEBUG_PREFIX . $msg); Logger::info(self::DEBUG_PREFIX . $msg);
$query = 'INSERT INTO ' . $this->tables[self::TABLE_SUM] . ' ' $query = 'INSERT INTO ' . $this->tables[self::TABLE_SUM] . ' '
. '(' . $this->escape_cols(['year', 'month', 'day', 'idp_id', 'sp_id', 'logins', 'users']) . ') ' . '(' . $this->escapeCols(['year', 'month', 'day', 'idp_id', 'sp_id', 'logins', 'users']) . ') '
. 'SELECT EXTRACT(YEAR FROM ' . $this->escape_col( . 'SELECT EXTRACT(YEAR FROM ' . $this->escapeCol(
'day' 'day'
) . '), EXTRACT(MONTH FROM ' . $this->escape_col( ) . '), EXTRACT(MONTH FROM ' . $this->escapeCol(
'day' 'day'
) . '), EXTRACT(DAY FROM ' . $this->escape_col('day') . '), '; ) . '), EXTRACT(DAY FROM ' . $this->escapeCol('day') . '), ';
foreach ($ids as $id) { foreach ($ids as $id) {
$query .= ($id === null ? '0' : $id) . ','; $query .= ($id === null ? '0' : $id) . ',';
} }
$query .= 'SUM(logins), COUNT(DISTINCT ' . $this->escape_col('user') . ') ' $query .= 'SUM(logins), COUNT(DISTINCT ' . $this->escapeCol('user') . ') '
. 'FROM ' . $this->tables[self::TABLE_PER_USER] . ' ' . 'FROM ' . $this->tables[self::TABLE_PER_USER] . ' '
. 'WHERE day<DATE(NOW()) ' . 'WHERE day<DATE(NOW()) '
. 'GROUP BY ' . $this->getAggregateGroupBy($ids) . ' '; . 'GROUP BY ' . $this->getAggregateGroupBy($ids) . ' ';
if ($this->isPgsql()) { if ($this->isPgsql()) {
$query .= 'ON CONFLICT (' . $this->escape_cols( $query .= 'ON CONFLICT (' . $this->escapeCols(
['year', 'month', 'day', 'idp_id', 'sp_id'] ['year', 'month', 'day', 'idp_id', 'sp_id']
) . ') DO NOTHING;'; ) . ') DO NOTHING;';
} elseif ($this->isMysql()) { } elseif ($this->isMysql()) {
...@@ -199,13 +212,13 @@ class DatabaseCommand ...@@ -199,13 +212,13 @@ class DatabaseCommand
$msg = 'Deleting detailed statistics'; $msg = 'Deleting detailed statistics';
Logger::info(self::DEBUG_PREFIX . $msg); Logger::info(self::DEBUG_PREFIX . $msg);
if ($this->isPgsql()) { if ($this->isPgsql()) {
$make_date = 'MAKE_DATE(' . $this->escape_cols(['year', 'month', 'day']) . ')'; $make_date = 'MAKE_DATE(' . $this->escapeCols(['year', 'month', 'day']) . ')';
$date_clause = sprintf('CURRENT_DATE - INTERVAL \'%s DAY\' ', $keepPerUserDays); $date_clause = sprintf('CURRENT_DATE - INTERVAL \'%s DAY\' ', $keepPerUserDays);
$params = []; $params = [];
} elseif ($this->isMysql()) { } elseif ($this->isMysql()) {
$make_date = 'STR_TO_DATE(CONCAT(' . $this->escape_col('year') . ",'-'," . $this->escape_col( $make_date = 'STR_TO_DATE(CONCAT(' . $this->escapeCol('year') . ",'-'," . $this->escapeCol(
'month' 'month'
) . ",'-'," . $this->escape_col('day') . "), '%Y-%m-%d')"; ) . ",'-'," . $this->escapeCol('day') . "), '%Y-%m-%d')";
$date_clause = 'CURDATE() - INTERVAL :days DAY'; $date_clause = 'CURDATE() - INTERVAL :days DAY';
$params = [ $params = [
'days' => $keepPerUserDays, 'days' => $keepPerUserDays,
...@@ -213,10 +226,10 @@ class DatabaseCommand ...@@ -213,10 +226,10 @@ class DatabaseCommand
} else { } else {
$this->unknownDriver(); $this->unknownDriver();
} }
$query = 'DELETE FROM ' . $this->tables[self::TABLE_PER_USER] . ' WHERE ' . $this->escape_col( $query = 'DELETE FROM ' . $this->tables[self::TABLE_PER_USER] . ' WHERE ' . $this->escapeCol(
'day' 'day'
) . ' < ' . $date_clause ) . ' < ' . $date_clause
. ' AND ' . $this->escape_col( . ' AND ' . $this->escapeCol(
'day' 'day'
) . ' IN (SELECT ' . $make_date . ' FROM ' . $this->tables[self::TABLE_SUM] . ')'; ) . ' IN (SELECT ' . $make_date . ' FROM ' . $this->tables[self::TABLE_SUM] . ')';
$written = $this->conn->write($query, $params); $written = $this->conn->write($query, $params);
...@@ -229,9 +242,32 @@ class DatabaseCommand ...@@ -229,9 +242,32 @@ class DatabaseCommand
} }
} }
public static function prependColon($str): string private function insertLogin($entities, $userId, $date)
{ {
return ':' . $str; foreach (Config::SIDES as $side) {
if (empty($entities[$side]['id'])) {
Logger::error(
self::DEBUG_PREFIX . 'idpEntityId or spEntityId is empty and login log was not inserted into the database.'
);
return;
}
}
$ids = [];
foreach (self::TABLE_SIDES as $side => $table) {
$tableIdColumn = self::TABLE_IDS[$table];
$ids[$tableIdColumn] = $this->getIdFromIdentifier($table, $entities[$side], $tableIdColumn);
}
if ($this->writeLogin($date, $ids, $userId) === false) {
Logger::error(self::DEBUG_PREFIX . 'The login log was not inserted.');
}
}
private function escapeCol($col_name): string
{
return $this->escape_char . $col_name . $this->escape_char;
} }
private function writeLogin($date, $ids, $user): bool private function writeLogin($date, $ids, $user): bool
...@@ -255,10 +291,10 @@ class DatabaseCommand ...@@ -255,10 +291,10 @@ class DatabaseCommand
]); ]);
$fields = array_keys($params); $fields = array_keys($params);
$placeholders = array_map(['self', 'prependColon'], $fields); $placeholders = array_map(['self', 'prependColon'], $fields);
$query = 'INSERT INTO ' . $this->tables[self::TABLE_PER_USER] . ' (' . $this->escape_cols($fields) . ')' . $query = 'INSERT INTO ' . $this->tables[self::TABLE_PER_USER] . ' (' . $this->escapeCols($fields) . ')' .
' VALUES (' . implode(', ', $placeholders) . ') '; ' VALUES (' . implode(', ', $placeholders) . ') ';
if ($this->isPgsql()) { if ($this->isPgsql()) {
$query .= 'ON CONFLICT (' . $this->escape_cols( $query .= 'ON CONFLICT (' . $this->escapeCols(
['day', 'idp_id', 'sp_id', 'user'] ['day', 'idp_id', 'sp_id', 'user']
) . ') DO UPDATE SET "logins" = ' . $this->tables[self::TABLE_PER_USER] . '.logins + 1;'; ) . ') DO UPDATE SET "logins" = ' . $this->tables[self::TABLE_PER_USER] . '.logins + 1;';
} elseif ($this->isMysql()) { } elseif ($this->isMysql()) {
...@@ -282,38 +318,6 @@ class DatabaseCommand ...@@ -282,38 +318,6 @@ class DatabaseCommand
return true; return true;
} }
private function prepareEntitiesData($request): array
{
$entities = [
Config::MODE_IDP => [],
Config::MODE_SP => [],
];
if ($this->mode !== Config::MODE_IDP && $this->mode !== Config::MODE_MULTI_IDP) {
$entities[Config::MODE_IDP][self::KEY_ID] = $this->getIdpIdentifier($request);
$entities[Config::MODE_IDP][self::KEY_NAME] = $this->getIdpName($request);
}
if ($this->mode !== Config::MODE_SP) {
$entities[Config::MODE_SP][self::KEY_ID] = $this->getSpIdentifier($request);
$entities[Config::MODE_SP][self::KEY_NAME] = $this->getSpName($request);
}
if ($this->mode !== Config::MODE_PROXY && $this->mode !== Config::MODE_MULTI_IDP) {
$entities[$this->mode] = $this->config->getSideInfo($this->mode);
if (empty($entities[$this->mode][self::KEY_ID]) || empty($entities[$this->mode][self::KEY_NAME])) {
Logger::error(self::DEBUG_PREFIX . 'Invalid configuration (id, name) for ' . $this->mode);
}
}
if ($this->mode === Config::MODE_MULTI_IDP) {
$entities[Config::MODE_IDP] = $this->config->getSideInfo(Config::MODE_IDP);
if (empty($entities[Config::MODE_IDP][self::KEY_ID]) || empty($entities[Config::MODE_IDP][self::KEY_NAME])) {
Logger::error(self::DEBUG_PREFIX . 'Invalid configuration (id, name) for ' . $this->mode);
}
}
return $entities;
}
private function getEntityDbIdFromEntityIdentifier($table, $entity, $idColumn) private function getEntityDbIdFromEntityIdentifier($table, $entity, $idColumn)
{ {
$identifier = $entity[self::KEY_ID]; $identifier = $entity[self::KEY_ID];
...@@ -362,6 +366,80 @@ class DatabaseCommand ...@@ -362,6 +366,80 @@ class DatabaseCommand
$query .= ' '; $query .= ' ';
} }
private function getEntities($request): array
{
$idpIdentifier = null;
$idpName = null;
$spIdentifier = null;
$spName = null;
if ($this->mode !== Config::MODE_IDP && $this->mode !== Config::MODE_MULTI_IDP) {
$idpIdentifier = $this->getIdpIdentifier($request);
$idpName = $this->getIdpName($request);
}
if ($this->mode !== Config::MODE_SP) {
$spIdentifier = $this->getSpIdentifier($request);
$spName = $this->getSpName($request);
}
return $this->prepareEntitiesStructure($idpIdentifier, $idpName, $spIdentifier, $spName);
}
private function prepareEntitiesStructure($idpIdentifier, $idpName, $spName, $spIdentifier): array
{
$entities = [
Config::MODE_IDP => [],
Config::MODE_SP => [],
];
if ($this->mode !== Config::MODE_IDP && $this->mode !== Config::MODE_MULTI_IDP) {
$entities[Config::MODE_IDP][self::KEY_ID] = $idpIdentifier;
$entities[Config::MODE_IDP][self::KEY_NAME] = $idpName;
}
if ($this->mode !== Config::MODE_SP) {
$entities[Config::MODE_SP][self::KEY_ID] = $spIdentifier;
$entities[Config::MODE_SP][self::KEY_NAME] = $spName;
}
if ($this->mode !== Config::MODE_PROXY && $this->mode !== Config::MODE_MULTI_IDP) {
$entities[$this->mode] = $this->config->getSideInfo($this->mode);
if (empty($entities[$this->mode][self::KEY_ID]) || empty($entities[$this->mode][self::KEY_NAME])) {
Logger::error('Invalid configuration (id, name) for ' . $this->mode);
}
}
if ($this->mode === Config::MODE_MULTI_IDP) {
$entities[Config::MODE_IDP] = $this->config->getSideInfo(Config::MODE_IDP);
if (empty($entities[Config::MODE_IDP][self::KEY_ID]) || empty($entities[Config::MODE_IDP][self::KEY_NAME])) {
Logger::error('Invalid configuration (id, name) for ' . $this->mode);
}
}
return $entities;
}
private function getIdFromIdentifier($table, $entity, $idColumn)
{
$identifier = $entity['id'];
$name = $entity['name'];
$query = 'INSERT INTO ' . $this->tables[$table] . '(identifier, name) VALUES (:identifier, :name1) ';
if ($this->conn->getDriver() === 'pgsql') {
$query .= 'ON CONFLICT (identifier) DO UPDATE SET name = :name2;';
} else {
$query .= 'ON DUPLICATE KEY UPDATE name = :name2';
}
$this->conn->write($query, [
'identifier' => $identifier,
'name1' => $name,
'name2' => $name,
]);
return $this->read('SELECT ' . $idColumn . ' FROM ' . $this->tables[$table]
. ' WHERE identifier=:identifier', [
'identifier' => $identifier,
])
->fetchColumn()
;
}
private function addDaysRange($days, &$query, &$params, $not = false) private function addDaysRange($days, &$query, &$params, $not = false)
{ {
if ($days !== 0) { // 0 = all time if ($days !== 0) { // 0 = all time
...@@ -392,12 +470,12 @@ class DatabaseCommand ...@@ -392,12 +470,12 @@ class DatabaseCommand
} }
} }
private function escape_col($col_name): string private function prependColon($str): string
{ {
return $this->escape_char . $col_name . $this->escape_char; return ':' . $str;
} }
private function escape_cols($col_names): string private function escapeCols($col_names): string
{ {
return $this->escape_char . implode( return $this->escape_char . implode(
$this->escape_char . ',' . $this->escape_char, $this->escape_char . ',' . $this->escape_char,
...@@ -414,7 +492,7 @@ class DatabaseCommand ...@@ -414,7 +492,7 @@ class DatabaseCommand
} }
} }
return $this->escape_cols($columns); return $this->escapeCols($columns);
} }
private function getIdpIdentifier($request) private function getIdpIdentifier($request)
......
<?php
declare(strict_types=1);
use SimpleSAML\Logger;
use SimpleSAML\Module\proxystatistics\Config;
use SimpleSAML\Module\proxystatistics\DatabaseCommand;
if ($_SERVER['REQUEST_METHOD'] !== 'POST' && $_SERVER['REQUEST_METHOD'] !== 'PUT') {
Logger::info(
'proxystatistics:writeLoginApi - API write called not using POST nor PUT, returning 405 response code'
);
header('HTTP/1.0 405 Method Not Allowed');
exit;
}
$config = Config::getInstance();
if (!$config->isApiWriteEnabled()) {
Logger::info(
'proxystatistics:writeLoginApi - API write called, but disabled in config. Returning 501 response code'
);
header('HTTP/1.0 501 Not Implemented');
exit;
}
$authUsername = $_SERVER['PHP_AUTH_USER'] ?? '';
$authPass = $_SERVER['PHP_AUTH_PW'] ?? '';
$username = $config->getApiWriteUsername();
$passwordHash = $config->getApiWritePasswordHash();
// If we get here, username was provided. Check password.
if ($authUsername !== $username || !password_verify($authPass, $passwordHash)) {
Logger::info(
'proxystatistics:writeLoginApi - API write called with bad credentials (' . $authUsername . ':' . $authPass . ') returning 401 response code'
);
header('HTTP/1.0 401 Unauthorized');
exit;
}
try {
$data = json_decode(file_get_contents('php://input'), true, 5, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
header('HTTP/1.0 400 Bad Request');
exit;
}
if (!empty(array_diff(
[
DatabaseCommand::API_USER_ID, DatabaseCommand::API_SERVICE_IDENTIFIER, DatabaseCommand::API_SERVICE_NAME, DatabaseCommand::API_IDP_IDENTIFIER, DatabaseCommand::API_IDP_NAME, ],
array_keys($data)
))) {
header('HTTP/1.0 400 Bad Request');
exit;
}
$dateTime = new DateTime();
$dbCmd = new DatabaseCommand();
try {
$dbCmd->insertLoginFromApi($data, $dateTime);
} catch (Exception $ex) {
Logger::error(
'proxystatistics:writeLoginApi - Caught exception while inserting login into statistics: ' . $ex->getMessage()
);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment