initial commit to enable token validation and key download
This commit is contained in:
+6
-2
@@ -1,5 +1,8 @@
|
|||||||
# Caddyfile for DigiErbe Tresor
|
# Caddyfile for DigiErbe Tresor
|
||||||
tresor.example.com {
|
tresor.example.com {
|
||||||
|
# enable TLS encryption
|
||||||
|
tls internal
|
||||||
|
|
||||||
#PHP
|
#PHP
|
||||||
php_fastcgi unix//run/php/php-fpm.sock
|
php_fastcgi unix//run/php/php-fpm.sock
|
||||||
|
|
||||||
@@ -7,8 +10,9 @@ tresor.example.com {
|
|||||||
root * /Pfad/Zu/DigiErbe/Tresor/public
|
root * /Pfad/Zu/DigiErbe/Tresor/public
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
basic_auth / {
|
basic_auth {
|
||||||
username $2a$14$SetHashedPasswordHere
|
ownerName $2a$14$SetHashedPasswordHere
|
||||||
|
allowedName $2a$14$SetHashedPasswordHere
|
||||||
}
|
}
|
||||||
|
|
||||||
# Activate file server in Caddy
|
# Activate file server in Caddy
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"ownerName": {
|
||||||
|
"myKey.pem": ["allowedName"]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"ownerName": {
|
|
||||||
"myKey.pem": ["alloweduser"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Regular → Executable
Regular → Executable
+62
-14
@@ -8,7 +8,8 @@ class TokenManager {
|
|||||||
/*
|
/*
|
||||||
* VARIABLES
|
* VARIABLES
|
||||||
*/
|
*/
|
||||||
private string $storagePath; // Subdirectory under Tresor's /private directory to use to store all the token information
|
private string $tokenStoragePath; // Subdirectory under Tresor's /public directory to use to store all the token information
|
||||||
|
private string $keyStoragePath; // Subdirectory under Tresor's /private directory to store all keyfiles
|
||||||
private int $expireDays; // number of dates for a token to expire
|
private int $expireDays; // number of dates for a token to expire
|
||||||
private int $maxDownloads; // maximum number of downloads before a token becomes invalid
|
private int $maxDownloads; // maximum number of downloads before a token becomes invalid
|
||||||
private int $tokenLength; // number of random bytes to use to create the token name
|
private int $tokenLength; // number of random bytes to use to create the token name
|
||||||
@@ -19,34 +20,44 @@ class TokenManager {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// initialize the token instance in PHP
|
// initialize the token instance in PHP
|
||||||
public __contruct(
|
public function __construct(
|
||||||
string $storagePath = '/tokens',
|
string $storagePath = '/tokens',
|
||||||
int $exireDays = 1,
|
string $keyStoragePath = '/keys',
|
||||||
|
int $expireDays = 1,
|
||||||
int $maxDownloads = 1,
|
int $maxDownloads = 1,
|
||||||
int $tokenLength = 32
|
int $tokenLength = 32
|
||||||
) {
|
) {
|
||||||
$this->storagePath = $storagePath;
|
$this->tokenStoragePath = __DIR__ . $storagePath;
|
||||||
$this->$expireDays = $expireDays;
|
$this->keyStoragePath = substr(__DIR__, 0, strrpos(__DIR__, '/') + 1) . 'private' . $keyStoragePath;
|
||||||
$this->$maxDownloads = $maxDownloads;
|
$this->expireDays = $expireDays;
|
||||||
$this->$tokenLength = $tokenLength;
|
$this->maxDownloads = $maxDownloads;
|
||||||
|
$this->tokenLength = $tokenLength;
|
||||||
|
|
||||||
|
if(!is_dir($this->tokenStoragePath))
|
||||||
|
mkdir($this->tokenStoragePath, 0755, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a new token and save it to the file system
|
// generate a new token and save it to the file system
|
||||||
public generateToken(
|
public function generateToken(
|
||||||
string $filename,
|
string $filename,
|
||||||
string $ownerName,
|
string $ownerName,
|
||||||
string $allowedUser
|
string $allowedUser
|
||||||
) {
|
) {
|
||||||
return (['error'=>'no token generated', 'status'=>'500']);
|
return (['error'=>'no token generated', 'status'=>500]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// retrieve/download the key file connected with the token
|
// retrieve/download the key file connected with the token
|
||||||
public retrieveToken(string $tokenName) {
|
public function retrieveToken(string $tokenName) {
|
||||||
|
$result = $this->validateToken($tokenName);
|
||||||
|
if(!isset($result['success']))
|
||||||
|
return ['error'=>'token invalid', 'status'=>422];
|
||||||
|
|
||||||
|
// send the file name of the designated key for download
|
||||||
|
return ['success'=>$result['success'], 'status'=>200];
|
||||||
}
|
}
|
||||||
|
|
||||||
// check a token and remove it when it's expired
|
// check a token and remove it when it's expired
|
||||||
public cleanupToken(string $tokenName) {
|
public function cleanupToken(string $tokenName) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,12 +66,49 @@ class TokenManager {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// check whether a token is still valid and accessible by the user requesting it
|
// check whether a token is still valid and accessible by the user requesting it
|
||||||
private validateToken($tokenName) {
|
private function validateToken($tokenName) {
|
||||||
return null;
|
// check if tokenName is a valid hexadecimal string
|
||||||
|
if(!ctype_xdigit($tokenName))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if token file exists and read data if so
|
||||||
|
$tokenPath = $this->getTokenFilePath($tokenName);
|
||||||
|
if(!file_exists($tokenPath))
|
||||||
|
return false;
|
||||||
|
$tokenData = json_decode(file_get_contents($tokenPath));
|
||||||
|
|
||||||
|
// check expiry of the token
|
||||||
|
if( new DateTime() > new DateTime($tokenData->expires))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// safety-check if requesting user name is matching the allowed characters (a-zA-Z0-9)
|
||||||
|
if(preg_match('/[^a-zA-Z0-9]/', $_SERVER['PHP_AUTH_USER']))
|
||||||
|
return false;
|
||||||
|
// check if the requesting user is allowed to query the token
|
||||||
|
if(levenshtein($_SERVER['PHP_AUTH_USER'], $tokenData->allowedUser))
|
||||||
|
return false;
|
||||||
|
// check if requesting user is allowed to retrieve the key
|
||||||
|
$allowedUsersFilePath = __DIR__ . '/../private/keys/allowedUsers.json';
|
||||||
|
if(!file_exists($allowedUsersFilePath))
|
||||||
|
return false;
|
||||||
|
$allowedUsers = json_decode(file_get_contents($allowedUsersFilePath))->{$tokenData->owner}->{$tokenData->file};
|
||||||
|
if(array_search($_SERVER['PHP_AUTH_USER'], $allowedUsers, true) === false)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//all checks completed successfully
|
||||||
|
$path = substr( __DIR__, 0, strrpos(__DIR__, '/') + 1 );
|
||||||
|
return ['success'=>$this->keyStoragePath . '/' . $tokenData->owner . '/' . $tokenData->file, 'status'=>200];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDownloadFileInfo ($tokenName) {
|
private function getDownloadFileInfo ($tokenName) {
|
||||||
$fileInfo = [];
|
$fileInfo = [];
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getTokenFilePath($tokenName) {
|
||||||
|
$filePath = $this->tokenStoragePath . '/' . $tokenName . '.json';
|
||||||
|
if(!file_exists($filePath))
|
||||||
|
return false;
|
||||||
|
return $filePath;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regular → Executable
+43
-7
@@ -12,13 +12,19 @@ $method = $_SERVER['REQUEST_METHOD'];
|
|||||||
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||||
$path = str_replace('/index.php', '', $path);
|
$path = str_replace('/index.php', '', $path);
|
||||||
$path = rtrim($path, '/');
|
$path = rtrim($path, '/');
|
||||||
|
$path = ltrim($path, '/');
|
||||||
|
|
||||||
|
$tokenManager = new TokenManager();
|
||||||
|
|
||||||
switch ($path) {
|
switch ($path) {
|
||||||
case '/request': // request access to another user's emergency / legacy key file
|
case 'request': // request access to another user's emergency / legacy key file
|
||||||
request_access();
|
requestAccess();
|
||||||
break;
|
break;
|
||||||
case 'deny': //deny access
|
case 'deny': // deny requested access
|
||||||
deny_access();
|
denyAccess();
|
||||||
|
break;
|
||||||
|
case 'download':
|
||||||
|
downloadKey();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
user_interface();
|
user_interface();
|
||||||
@@ -29,20 +35,50 @@ function ReturnJsonResponse($data, $status = 200) {
|
|||||||
http_response_code($status);
|
http_response_code($status);
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||||
echo json_encode($data);
|
print json_encode($data);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
function request_access() {
|
function requestAccess() {
|
||||||
$data = ['request access' => 'request not allowed'];
|
$data = ['request access' => 'request not allowed'];
|
||||||
ReturnJsonResponse($data, 403);
|
ReturnJsonResponse($data, 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deny_access() {
|
function denyAccess() {
|
||||||
$data = ['deny access'=>'emergency user request revoked'];
|
$data = ['deny access'=>'emergency user request revoked'];
|
||||||
ReturnJsonResponse($data);
|
ReturnJsonResponse($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downloadKey() {
|
||||||
|
global $tokenManager;
|
||||||
|
if(!isset($_REQUEST['token']) || !ctype_xdigit($_REQUEST['token']))
|
||||||
|
ReturnJsonResponse(['error'=>'missing or invalid token'], 400);
|
||||||
|
$result = $tokenManager->retrieveToken($_REQUEST['token']);
|
||||||
|
if(isset($result['error']))
|
||||||
|
ReturnJsonResponse(['error'=>$result['error']], $result['status']);
|
||||||
|
|
||||||
|
if(!isset($result['success']))
|
||||||
|
ReturnJsonResponse(['error'=>'token could not be validated'], 400);
|
||||||
|
|
||||||
|
//check if returned keyfile exists
|
||||||
|
if(!file_exists($result['success']))
|
||||||
|
ReturnJsonResponse(['error'=>'keyfile not found'], 404);
|
||||||
|
|
||||||
|
$filePath = $result['success'];
|
||||||
|
$fileName = basename($filePath);
|
||||||
|
$fileSize = filesize($filePath);
|
||||||
|
|
||||||
|
header('Content-Type: application/octet-stream');
|
||||||
|
header("Content-Disposition: attachment; filename=\"{$fileName}\"");
|
||||||
|
header("Content-Length: {$fileSize}");
|
||||||
|
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||||
|
header('Pragma: no-cache');
|
||||||
|
header('Expires: 0');
|
||||||
|
|
||||||
|
readfile($filePath);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
function user_interface() {
|
function user_interface() {
|
||||||
print('<!DOCTYPE html><html><head><title>DigiErbe Tresor</title></head><body><h1>Willkommen beim DigiErbe Tresor</h1></body></html>');
|
print('<!DOCTYPE html><html><head><title>DigiErbe Tresor</title></head><body><h1>Willkommen beim DigiErbe Tresor</h1></body></html>');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"requested_key": "myKey.pem",
|
||||||
|
"created": "2026-04-04T12:00:00+00:00"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user