OTP, Groups, Grants,
This commit is contained in:
parent
04f2123bc6
commit
972eeae7d3
138
src/Tor/Api.php
138
src/Tor/Api.php
@ -2,16 +2,24 @@
|
||||
|
||||
namespace Tor;
|
||||
|
||||
use Tor\Controller\ServiceApi;
|
||||
use Tor\Controller\GrantApi;
|
||||
use Tor\Controller\TicketApi;
|
||||
|
||||
use Tor\Controller\ApiAuth;
|
||||
|
||||
use Gac\Routing\Request;
|
||||
use Gac\Routing\Routes;
|
||||
|
||||
// Implements the dispatcher for Tor's API
|
||||
class Api {
|
||||
public function init() {
|
||||
global $routes;
|
||||
|
||||
$middlewareE = [ [ApiAuth::class, 'authExternal'] ];
|
||||
$middlewareI = [ [ApiAuth::class, 'authInternal'] ];
|
||||
$middlewareS = [ [ApiAuth::class, 'authSystem'] ];
|
||||
|
||||
// Respond with supported legacy versions and current version of API
|
||||
$routes->add('/api', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
@ -20,93 +28,109 @@ class Api {
|
||||
|
||||
$routes->add('/api/v1', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'unauthorized' ]);
|
||||
});
|
||||
|
||||
// Internal: List all users
|
||||
$routes->add('/api/v1/user/list', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
|
||||
// Internal: Assign a given new public key id to username
|
||||
// No Action specified
|
||||
$routes->middleware($middlewareS)->add('/api/v1/user', [ UserApi::class, 'response' ]);
|
||||
|
||||
// System: Assign a given new public key id and cert to username
|
||||
// Creates user if it doesn't exist
|
||||
$routes->add('/api/v1/user/assign', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
$routes->middleware($middlewareS)->add('/api/v1/user/provision/{userName}', [ UserApi::class, 'provision' ]);
|
||||
|
||||
// Internal: Delete User and all Sessions + ExtraData
|
||||
$routes->add('/api/v1/user/delete/{username}', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
// System: Delete User and all Sessions + ExtraData
|
||||
$routes->middleware($middlewareS)->add('/api/v1/user/delete/{userName}', [ UserApi::class, 'delete' ]);
|
||||
|
||||
// Internal: Create a new permission group
|
||||
$routes->add('/api/v1/group/create/{name}', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
// System: Fetch info about user
|
||||
$routes->middleware($middlewareS)->add('/api/v1/user/info/{userName}', [ UserApi::class, 'info' ]);
|
||||
|
||||
// Internal: Delete a permission group
|
||||
$routes->add('/api/v1/group/delete/{name}', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
// System: List all users
|
||||
$routes->middleware($middlewareS)->add('/api/v1/user/list', [ UserApi::class, 'list' ]);
|
||||
|
||||
// Internal: Assign User to Group
|
||||
$routes->add('/api/v1/group/assign/{name}', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
// No Action specified
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group', [ GroupApi::class, 'response' ]);
|
||||
|
||||
// Internal: Revoke User's Group Membership
|
||||
$routes->add('/api/v1/group/revoke', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
// System: Change properties of group, like default provisioning for ex.
|
||||
// If not exists: Create a new permission group
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/provision/{groupName}', [ GroupApi::class, 'provision' ]);
|
||||
|
||||
// System: Delete a permission group
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/delete/{groupName}', [ GroupApi::class, 'delete' ]);
|
||||
|
||||
// System: Assign User to Group
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/assign/{groupName}', [ GroupApi::class, 'assign' ]);
|
||||
|
||||
// System: Revoke User's Group Membership
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/revoke/{groupName}', [ GroupApi::class, 'revoke' ]);
|
||||
|
||||
// Fetch info about group
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/info/{groupName}', [ GroupApi::class, 'info' ]);
|
||||
|
||||
// List all groups
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/list', [ GroupApi::class, 'list' ]);
|
||||
|
||||
// List all groups by service
|
||||
$routes->middleware($middlewareS)->add('/api/v1/group/list/{serviceName}', [ GroupApi::class, 'listByService' ]);
|
||||
|
||||
// Internal: Delete User and all Sessions + ExtraData
|
||||
$routes->add('/api/v1/group/delete', function (Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
});
|
||||
|
||||
// No action specified
|
||||
$routes->add('/api/v1/grant', [ GrantApi::class, 'response' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/service', [ ServiceApi::class, 'response' ]);
|
||||
|
||||
// System: Provision a new token or api level for a given service
|
||||
// Creates Service if it does not exist
|
||||
$routes->middleware($middlewareS)->add('/api/v1/service/provision/{serviceName}', [ ServiceApi::class, 'provision' ]);
|
||||
|
||||
// System: Delete a service and all grants, tickets depending on it
|
||||
$routes->middleware($middlewareS)->add('/api/v1/service/delete/{serviceName}', [ ServiceApi::class, 'delete' ]);
|
||||
|
||||
// System: List info about a service
|
||||
$routes->middleware($middlewareS)->add('/api/v1/service/info/{serviceName}', [ ServiceApi::class, 'info' ]);
|
||||
|
||||
// System: List all services
|
||||
$routes->middleware($middlewareS)->add('/api/v1/service/list', [ ServiceApi::class, 'list' ]);
|
||||
|
||||
// ---------------------------------------------------- //
|
||||
|
||||
// No action specified
|
||||
$routes->middleware($middlewareE)->add('/api/v1/grant', [ GrantApi::class, 'response' ]);
|
||||
|
||||
// Create Ticket Granting Ticket
|
||||
$routes->add('/api/v1/grant/create', [ GrantApi::class, 'create' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/grant/create', [ GrantApi::class, 'create' ]);
|
||||
|
||||
// Invalidate Ticket Granting Ticket
|
||||
$routes->add('/api/v1/grant/destroy/{grantID}', [ GrantApi::class, 'destroy' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/grant/destroy/{grantID}', [ GrantApi::class, 'destroy' ]);
|
||||
|
||||
// Poll Authorization Status of Grant
|
||||
$routes->add('/api/v1/grant/status/{grantID}', [ GrantApi::class, 'status' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/grant/status/{grantID}', [ GrantApi::class, 'status' ]);
|
||||
|
||||
// Internal: List all active Grants
|
||||
$routes->add('/api/v1/grant/list', [ GrantApi::class, 'list' ]);
|
||||
// Internal: List all active Grants for current service
|
||||
$routes->middleware($middlewareI)->add('/api/v1/grant/list', [ GrantApi::class, 'list' ]);
|
||||
|
||||
// Internal: Approve of Grant with specified subset of extradata
|
||||
$routes->add('/api/v1/grant/approve/{grantID}', [ GrantApi::class, 'approve' ]);
|
||||
// System: Approve of Grant with specified subset of extradata
|
||||
$routes->middleware($middlewareS)->add('/api/v1/grant/approve/{grantID}', [ GrantApi::class, 'approve' ]);
|
||||
|
||||
// Internal: Reject Grant
|
||||
$routes->add('/api/v1/grant/reject/{grantID}', [ GrantApi::class, 'reject' ]);
|
||||
$routes->middleware($middlewareI)->add('/api/v1/grant/reject/{grantID}', [ GrantApi::class, 'reject' ]);
|
||||
|
||||
// Fetch ID of Session Ticket by using authorized Grant ID
|
||||
// TODO: Decision
|
||||
|
||||
// No action specified
|
||||
$routes->middleware($middlewareE)->add('/api/v1/ticket', [ TicketApi::class, 'response' ]);
|
||||
|
||||
// Fetch ID of Session Ticket by using authorized Grant ID => NEW: Decision OTP
|
||||
// Useful when polling manually, not neccessary when using callback
|
||||
$routes->add('/api/v1/ticket/fetch/{grantID}', [ TicketApi::class, 'fetch' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/ticket/fetch/{grantID}', [ TicketApi::class, 'fetch' ]);
|
||||
|
||||
// Destroy active Session
|
||||
$routes->add('/api/v1/ticket/destroy/{ticketID}', [ TicketApi::class, 'destroy' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/ticket/destroy/{ticketID}', [ TicketApi::class, 'destroy' ]);
|
||||
|
||||
// Poll authorization status and start / end date for session ticket
|
||||
$routes->add('/api/v1/ticket/status/{ticketID}', [ TicketApi::class, 'status' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/ticket/status/{ticketID}', [ TicketApi::class, 'status' ]);
|
||||
|
||||
// Session KeepAlive
|
||||
$routes->add('/api/v1/ticket/heartbeat/{ticketID}', [ TicketApi::class, 'heartbeat' ]);
|
||||
$routes->middleware($middlewareE)->add('/api/v1/ticket/heartbeat/{ticketID}', [ TicketApi::class, 'heartbeat' ]);
|
||||
|
||||
// Internal: List all active Session Tickets
|
||||
$routes->add('/api/v1/ticket/list', [ TicketApi::class, 'list' ]);
|
||||
$routes->middleware($middlewareI)->add('/api/v1/ticket/list', [ TicketApi::class, 'list' ]);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,18 @@ class Utils {
|
||||
$path = ( $path !== '/' ) ? rtrim($path, '/') : $path;
|
||||
return ( $position === false ) ? $path : substr($path, 0, $position);
|
||||
}
|
||||
|
||||
public static function FetchPostData(): array | bool
|
||||
{
|
||||
// POST
|
||||
$post_data = file_get_contents('php://input');
|
||||
|
||||
// Check if post data exists and auth json node is present
|
||||
if ($post_data != false)
|
||||
return json_decode($post_data, true)['data'] ?? false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the browser provided a valid SSL client certificate
|
||||
@ -37,5 +49,44 @@ class Utils {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function OTPFactory(int $length = 6, bool $alpha = false): string
|
||||
{
|
||||
// Take a generator string which consist of
|
||||
// all numeric digits
|
||||
// or all alpha numeric chars
|
||||
if (!$alpha)
|
||||
$generator = "1357902468";
|
||||
else
|
||||
$generator = "qwerASDFGHJKLtzuioQWERTZUIOPpasdfgh102938jklyxcYXCVBNMvbnm4756";
|
||||
|
||||
// Iterate $length times and pick a single character
|
||||
// from generator and append it to $result
|
||||
|
||||
// Login for generating a random character from generator
|
||||
// ---generate a random number
|
||||
// ---take modulus of same with length of generator (say i)
|
||||
// ---append the character at place (i) from generator to result
|
||||
|
||||
$result = "";
|
||||
|
||||
for ($i = 1; $i <= $length; $i++) {
|
||||
$result .= substr($generator, (rand()%(strlen($generator))), 1);
|
||||
}
|
||||
|
||||
// Return result
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function TokenFactory(int $blocks = 4): string
|
||||
{
|
||||
$token = "";
|
||||
|
||||
// Generate a token consisting out of $blocks many blocks
|
||||
for ($i = 1; $i <= $blocks; $i++)
|
||||
$token = self::OTPFactory(length: 4, alpha: true) . '-' . $token;
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
109
src/Tor/Controller/ApiAuth.php
Normal file
109
src/Tor/Controller/ApiAuth.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Tor\Controller;
|
||||
|
||||
use Tor\Common\Utils;
|
||||
use Tor\Data\AuthLevel;
|
||||
use Tor\Data\Service;
|
||||
|
||||
use Gac\Routing\Request;
|
||||
|
||||
class ApiAuth {
|
||||
|
||||
// Returns decoded array or false if no authorization header/parameter found
|
||||
private static function FetchAuth()
|
||||
{
|
||||
// TODO: Use Authorization Token Header or similar concepts
|
||||
|
||||
// POST
|
||||
$post_data = file_get_contents('php://input');
|
||||
|
||||
// Check if post data exists and auth json node is present
|
||||
if ($post_data != false)
|
||||
$post_data = json_decode($post_data, true)["auth"] ?? false;
|
||||
|
||||
// GET
|
||||
if ($post_data == false && isset($_GET["service"]) && isset($_GET["token"]))
|
||||
$post_data = array("service" => $_GET["service"], "token" => $_GET["token"]);
|
||||
|
||||
if ($post_data != false)
|
||||
{
|
||||
$srv_value = trim($post_data['service'] ?? "");
|
||||
$token_value = trim($post_data['token'] ?? "");
|
||||
|
||||
if (!empty($srv_value) && !empty($token_value))
|
||||
return array("service" => $srv_value, "token" => $token_value);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function ProcessAuth()
|
||||
{
|
||||
global $data, $api_service;
|
||||
|
||||
// We already have acquired authorization
|
||||
// INFO: This might not be instance safe
|
||||
if (isset($api_service))
|
||||
return;
|
||||
|
||||
$authData = self::fetchAuth();
|
||||
|
||||
// Check if service exists
|
||||
if ($authData != false && $data->exists(Service::class, $authData['service']))
|
||||
{
|
||||
$service = $data->load(Service::class, $authData['service']);
|
||||
if ($service->token != null && !empty(trim($service->token)))
|
||||
{
|
||||
// Check if authorization token matches
|
||||
if ($service->token == $authData['token'])
|
||||
$api_service = $service;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function authExternal(\Gac\Routing\Request $request)
|
||||
{
|
||||
$this->auth($request, AuthLevel::External);
|
||||
}
|
||||
|
||||
public function authInternal(\Gac\Routing\Request $request)
|
||||
{
|
||||
$this->auth($request, AuthLevel::Internal);
|
||||
}
|
||||
|
||||
public function authSystem(\Gac\Routing\Request $request)
|
||||
{
|
||||
$this->auth($request, AuthLevel::System);
|
||||
}
|
||||
|
||||
// Middleware for api authentication checking
|
||||
private function auth(\Gac\Routing\Request $request, AuthLevel $minLevel = AuthLevel::External)
|
||||
{
|
||||
if (!self::MatchesLevel($minLevel)) {
|
||||
$request->status(401, 'Unauthorized')->send(["error" => ["message" => "unauthorized"]]);
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns current service if authorization successful
|
||||
public static function GetService(): Service | null
|
||||
{
|
||||
global $api_service;
|
||||
|
||||
self::ProcessAuth();
|
||||
|
||||
return $api_service ?? null;
|
||||
}
|
||||
|
||||
public static function MatchesLevel(AuthLevel $level): bool
|
||||
{
|
||||
if (self::GetService() != null
|
||||
&& self::GetService()->level->value >= $level->value)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -7,6 +7,8 @@ use Tor\Data\Ticket;
|
||||
use Tor\Data\User;
|
||||
use Tor\Data\Service;
|
||||
|
||||
use Tor\Controller\ApiAuth;
|
||||
|
||||
use Gac\Routing\Request;
|
||||
|
||||
class GrantApi extends BaseApi {
|
||||
@ -21,20 +23,21 @@ class GrantApi extends BaseApi {
|
||||
while ($data->exists(Grant::class , $uniqid))
|
||||
$uniqid = uniqid();
|
||||
|
||||
// TODO: Services
|
||||
$service = new Service();
|
||||
$service->id = uniqid();
|
||||
|
||||
$grant = new Grant();
|
||||
$grant->id = $uniqid;
|
||||
$grant->service = $service;
|
||||
$grant->service = ApiAuth::GetService();
|
||||
$grant->create = time();
|
||||
|
||||
$data->save($grant);
|
||||
$data->save($service);
|
||||
|
||||
$request->status(201, 'Created')
|
||||
->send([ 'grant' => $grant->id, 'service' => $grant->service->flatIdentifier(), 'result' => 'created' ]);
|
||||
$response = [
|
||||
'grant' => $grant->id,
|
||||
'service' => $grant->service->flatIdentifier(),
|
||||
'create' => $grant->create,
|
||||
'result' => 'created'
|
||||
];
|
||||
|
||||
$request->status(201, 'Created')->send($response);
|
||||
}
|
||||
|
||||
// Destroy a grant and all dependencies
|
||||
@ -59,7 +62,7 @@ class GrantApi extends BaseApi {
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: Approve Grant (Only works if not authorized yet)
|
||||
// System: Approve Grant (Only works if not authorized yet)
|
||||
function approve(\Gac\Routing\Request $request, string $grantID) {
|
||||
global $data;
|
||||
|
||||
@ -147,13 +150,18 @@ class GrantApi extends BaseApi {
|
||||
function list(\Gac\Routing\Request $request) {
|
||||
global $data;
|
||||
|
||||
// TODO:
|
||||
$serviceName = '638bcc3500810';
|
||||
// Get name of authenticated service
|
||||
$serviceName = ApiAuth::GetService()->flatIdentifier();
|
||||
|
||||
$grants = $data->fromIndex(Service::class, $serviceName, findClass: Grant::class);
|
||||
|
||||
$result = [
|
||||
'grants' => $grants,
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'grants' => $grants, 'result' => 'ok' ]);
|
||||
->send($result);
|
||||
}
|
||||
|
||||
// Default response if no action is defined
|
||||
|
137
src/Tor/Controller/ServiceApi.php
Normal file
137
src/Tor/Controller/ServiceApi.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Tor\Controller;
|
||||
|
||||
use Tor\Data\Grant;
|
||||
use Tor\Data\Ticket;
|
||||
use Tor\Data\Service;
|
||||
use Tor\Data\AuthLevel;
|
||||
|
||||
use Tor\Common\Utils;
|
||||
|
||||
use Gac\Routing\Request;
|
||||
|
||||
class ServiceApi extends BaseApi {
|
||||
|
||||
// System: Provision new token or auth level for service
|
||||
// Create service if it doesn't exist
|
||||
function provision(\Gac\Routing\Request $request, string $serviceName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Service::class, $serviceName);
|
||||
|
||||
$post_data = Utils::FetchPostData();
|
||||
|
||||
if ($post_data != false) {
|
||||
$new_token = trim($post_data['token'] ?? "");
|
||||
$new_level = trim($post_data['level'] ?? "");
|
||||
}
|
||||
|
||||
if (!$exists) {
|
||||
$service = new Service();
|
||||
$service->id = $serviceName;
|
||||
$service->level = AuthLevel::External;
|
||||
$service->token = Utils::TokenFactory();
|
||||
} else {
|
||||
$service = $data->load(Service::class, $serviceName);
|
||||
}
|
||||
|
||||
if (!empty($new_token))
|
||||
$service->token = $new_token;
|
||||
|
||||
if (!empty($new_level))
|
||||
$service->level = AuthLevel::from($new_level);
|
||||
|
||||
$data->save($service);
|
||||
|
||||
$result = [
|
||||
'service' => $service->flatIdentifier(),
|
||||
'token' => $service->token,
|
||||
'level' => $service->level->value,
|
||||
];
|
||||
|
||||
if (!$exists) {
|
||||
$result['result'] = 'created';
|
||||
$request->status(201, 'Created')
|
||||
->send($result);
|
||||
} else {
|
||||
$result['result'] = 'provisioned';
|
||||
$request->status(200, 'OK')
|
||||
->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// System: Delete a service and all dependencies
|
||||
function delete(\Gac\Routing\Request $request, string $serviceName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Service::class, $serviceName);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$grants = $data->fromIndex(Service::class, $serviceName, findClass: Grant::class);
|
||||
|
||||
if (!empty($grants))
|
||||
{
|
||||
foreach ($grants as $grant)
|
||||
{
|
||||
$tickets = $data->fromIndex(Grant::class, $grant->flatIdentifier(), findClass: Ticket::class);
|
||||
|
||||
$data->delete(Grant::class, $grant->flatIdentifier());
|
||||
|
||||
foreach ($tickets as $ticket)
|
||||
$data->delete(Ticket::class, $ticket->flatIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
$data->delete(Service::class, $serviceName);
|
||||
$request->status(200, 'OK')->send([ 'result' => 'destroyed' ]);
|
||||
}
|
||||
}
|
||||
|
||||
// System: Fetch info about a Service
|
||||
function info(\Gac\Routing\Request $request, string $serviceName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Service::class, $serviceName);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$service = $data->load(Service::class, $serviceName);
|
||||
|
||||
$result = [
|
||||
'service' => $service->flatIdentifier(),
|
||||
'token' => $service->token,
|
||||
'level' => $service->level->value,
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
$request->status(200, 'OK')->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// System: List all Services
|
||||
function list(\Gac\Routing\Request $request) {
|
||||
global $data;
|
||||
|
||||
$services = $data->listAll(Service::class);
|
||||
|
||||
$result = [
|
||||
'services' => $services,
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
$request->status(200, 'OK')
|
||||
->send($result);
|
||||
}
|
||||
|
||||
// Default response if no action is defined
|
||||
function response(\Gac\Routing\Request $request) {
|
||||
$request->status(200, 'OK')->send([ 'result' => 'ok' ]);
|
||||
}
|
||||
|
||||
}
|
@ -10,54 +10,94 @@ use Gac\Routing\Request;
|
||||
class TicketApi extends BaseApi {
|
||||
|
||||
// Fetch ID of Session Ticket by using authorized Grant ID
|
||||
function fetch(\Gac\Routing\Request $request) {
|
||||
function fetch(\Gac\Routing\Request $request, string $grantId) {
|
||||
global $data;
|
||||
|
||||
// TODO: Identifier
|
||||
$identifier = 'cats';
|
||||
$exists = $data->exists(Grant::class, $identifier);
|
||||
$ticket = $data->fromIndex(Grant::class, $id, Ticket::class)[0] ?? null;
|
||||
|
||||
if (!$exists)
|
||||
if ($ticket == null)
|
||||
$request->status(200, 'OK')->send([ 'ticket' => $ticket->id, 'result' => 'found' ]);
|
||||
else
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
else {
|
||||
// TODO: Create Ticket on Approval instead and ... load by index
|
||||
$ticket = new Ticket();
|
||||
$ticket->grant = $data->load(Grant::class, $identifier);
|
||||
$ticket->user = new User();
|
||||
$ticket->start = time();
|
||||
$ticket->end = time() + 86400;
|
||||
|
||||
$data->save($ticket);
|
||||
|
||||
$request->status(201, 'Created')
|
||||
->send([ 'ticket' => $ticket->id, 'result' => 'created' ]);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy active session
|
||||
function destroy(\Gac\Routing\Request $request) {
|
||||
function destroy(\Gac\Routing\Request $request, string $ticketId) {
|
||||
global $data;
|
||||
|
||||
// TODO: Identifier
|
||||
$identifier = 'cats';
|
||||
$exists = $data->exists(Grant::class, $identifier);
|
||||
$exists = $data->exists(Ticket::class, $ticketId);
|
||||
|
||||
if (!$exists)
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
else
|
||||
$data->delete(Grant::class, $identifier);
|
||||
} else {
|
||||
$ticket = $data->load(Ticket::class, $ticketId);
|
||||
$data->delete(Ticket::class, $ticketId);
|
||||
|
||||
// Also delete grant
|
||||
$data->delete(Grant::class, $ticket->grant->flatIdentifier());
|
||||
|
||||
$request->status(200, 'OK')->send([ 'result' => 'destroyed' ]);
|
||||
}
|
||||
}
|
||||
|
||||
// Poll authorization status and start / end date for session ticket
|
||||
function status(\Gac\Routing\Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
function status(\Gac\Routing\Request $request, string $ticketId) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Ticket::class, $ticketId);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$ticket = $data->load(Ticket::class, $ticketId);
|
||||
|
||||
$result = [
|
||||
'user' => $ticket->user->flatIdentifier(),
|
||||
'grant' => $ticket->grant->flatIdentifier(),
|
||||
'start' => $ticket->start,
|
||||
'end' = $ticket->end,
|
||||
'status' => 'valid',
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
if ($ticket->end < time())
|
||||
$result["status"] = "expired";
|
||||
|
||||
$request->status(200, 'OK')->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Session KeepAlive
|
||||
function heartbeat(\Gac\Routing\Request $request) {
|
||||
$request->status(200, 'OK')
|
||||
->send([ 'result' => 'ok' ]);
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Ticket::class, $ticketId);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$ticket = $data->load(Ticket::class, $ticketId);
|
||||
|
||||
$result = [
|
||||
'start' => $ticket->start,
|
||||
'end' = $ticket->end,
|
||||
'result' => 'expired'
|
||||
];
|
||||
|
||||
if ($ticket->end >= time())
|
||||
{
|
||||
$ticket->end = time() + 86400;
|
||||
$data->save($ticket);
|
||||
|
||||
$result['end'] = $ticket->end;
|
||||
$result['result'] = 'ok';
|
||||
|
||||
}
|
||||
|
||||
$request->status(200, 'OK')->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// Internal: List all tickets
|
||||
|
144
src/Tor/Controller/UserApi.php
Normal file
144
src/Tor/Controller/UserApi.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace Tor\Controller;
|
||||
|
||||
use Tor\Data\Group;
|
||||
use Tor\Data\User;
|
||||
|
||||
use Gac\Routing\Request;
|
||||
|
||||
class UserApi extends BaseApi {
|
||||
|
||||
// System: Provision new token or auth level for service
|
||||
// Create service if it doesn't exist
|
||||
function provision(\Gac\Routing\Request $request, string $serviceName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(Service::class, $serviceName);
|
||||
|
||||
$post_data = Utils::FetchPostData();
|
||||
|
||||
if ($post_data != false) {
|
||||
$new_token = trim($post_data['token'] ?? "");
|
||||
$new_level = trim($post_data['level'] ?? "");
|
||||
}
|
||||
|
||||
if (!$exists) {
|
||||
$service = new Service();
|
||||
$service->id = $serviceName;
|
||||
$service->level = AuthLevel::External;
|
||||
$service->token = Utils::TokenFactory();
|
||||
} else {
|
||||
$service = $data->load(Service::class, $serviceName);
|
||||
}
|
||||
|
||||
if (!empty($new_token))
|
||||
$service->token = $new_token;
|
||||
|
||||
if (!empty($new_level))
|
||||
$service->level = AuthLevel::from($new_level);
|
||||
|
||||
$data->save($service);
|
||||
|
||||
$result = [
|
||||
'service' => $service->flatIdentifier(),
|
||||
'token' => $service->token,
|
||||
'level' => $service->level->value,
|
||||
];
|
||||
|
||||
if (!$exists) {
|
||||
$result['result'] = 'created';
|
||||
$request->status(201, 'Created')
|
||||
->send($result);
|
||||
} else {
|
||||
$result['result'] = 'provisioned';
|
||||
$request->status(200, 'OK')
|
||||
->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// System: Delete an User and all dependencies
|
||||
function delete(\Gac\Routing\Request $request, string $userName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(User::class, $userName);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$tickets = $data->fromIndex(User::class, $userName, findClass: Tickets::class);
|
||||
|
||||
if (!empty($tickets))
|
||||
{
|
||||
foreach ($tickets as $ticket)
|
||||
{
|
||||
$grants = $data->fromIndex(Ticket::class, $ticket->flatIdentifier(), findClass: Grant::class);
|
||||
|
||||
$data->delete(Ticket::class, $ticket->flatIdentifier());
|
||||
|
||||
foreach ($grants as $grant)
|
||||
$data->delete(Grant::class, $grant->flatIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
$data->delete(User::class, $userName);
|
||||
$request->status(200, 'OK')->send([ 'result' => 'destroyed' ]);
|
||||
}
|
||||
}
|
||||
|
||||
// System: Fetch info about an User
|
||||
function info(\Gac\Routing\Request $request, string $userName) {
|
||||
global $data;
|
||||
|
||||
$exists = $data->exists(User::class, $userName);
|
||||
|
||||
if (!$exists) {
|
||||
$request->status(404, 'Not Found')->send(["error" => ["message" => "not found"]]);
|
||||
}
|
||||
else {
|
||||
$user = $data->load(User::class, $userName);
|
||||
|
||||
$groups = [];
|
||||
|
||||
if (isset($user->groups) && $user->groups != null)
|
||||
{
|
||||
foreach ($user->groups as $group)
|
||||
$groups[] = $group->flatIdentifier();
|
||||
}
|
||||
|
||||
$result = [
|
||||
'user' => $user->flatIdentifier(),
|
||||
'cert' => $user->cert,
|
||||
'serial' => $user->serial,
|
||||
'create' => $user->create,
|
||||
'expire' => $user->expire,
|
||||
'groups' => $groups,
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
$request->status(200, 'OK')->send($result);
|
||||
}
|
||||
}
|
||||
|
||||
// System: List all Users
|
||||
function list(\Gac\Routing\Request $request) {
|
||||
global $data;
|
||||
|
||||
$users = $data->listAll(User::class);
|
||||
|
||||
$result = [
|
||||
'users' => $users,
|
||||
'result' => 'ok'
|
||||
];
|
||||
|
||||
$request->status(200, 'OK')
|
||||
->send($result);
|
||||
}
|
||||
|
||||
// Default response if no action is defined
|
||||
function response(\Gac\Routing\Request $request) {
|
||||
$request->status(200, 'OK')->send([ 'result' => 'ok' ]);
|
||||
}
|
||||
|
||||
}
|
11
src/Tor/Data/AuthLevel.php
Normal file
11
src/Tor/Data/AuthLevel.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
namespace Tor\Data;
|
||||
|
||||
enum AuthLevel: int
|
||||
{
|
||||
case Disabled = -1;
|
||||
case External = 0;
|
||||
case Internal = 1;
|
||||
case System = 9;
|
||||
}
|
||||
?>
|
11
src/Tor/Data/Decision.php
Normal file
11
src/Tor/Data/Decision.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Tor\Data;
|
||||
|
||||
use Tor\Data\Status;
|
||||
|
||||
class Decision extends BaseEntity {
|
||||
public string $otp;
|
||||
public int $date;
|
||||
public Status $result;
|
||||
}
|
@ -2,7 +2,11 @@
|
||||
|
||||
namespace Tor\Data;
|
||||
|
||||
use Tor\Data\Service;
|
||||
use Tor\Data\Status;
|
||||
|
||||
class Grant extends BaseEntity {
|
||||
public Service $service;
|
||||
public Status $status;
|
||||
public int $create;
|
||||
}
|
||||
|
10
src/Tor/Data/Group.php
Normal file
10
src/Tor/Data/Group.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tor\Data;
|
||||
|
||||
use Tor\Data\Service;
|
||||
|
||||
class Group extends BaseEntity {
|
||||
public Service $service;
|
||||
public bool $default;
|
||||
}
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace Tor\Data;
|
||||
|
||||
use Tor\Data\AuthLevel;
|
||||
|
||||
class Service extends BaseEntity {
|
||||
public string $token;
|
||||
public AuthLevel $level;
|
||||
}
|
||||
|
10
src/Tor/Data/Status.php
Normal file
10
src/Tor/Data/Status.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
namespace Tor\Data;
|
||||
|
||||
enum Status: int
|
||||
{
|
||||
case Pending = -1;
|
||||
case Rejected = 0;
|
||||
case Granted = 1;
|
||||
}
|
||||
?>
|
@ -2,6 +2,9 @@
|
||||
|
||||
namespace Tor\Data;
|
||||
|
||||
use Tor\Data\Grant;
|
||||
use Tor\Data\User;
|
||||
|
||||
class Ticket extends BaseEntity {
|
||||
public Grant $grant;
|
||||
public User $user;
|
||||
|
@ -5,4 +5,10 @@ namespace Tor\Data;
|
||||
class User extends BaseEntity {
|
||||
public string $serial;
|
||||
public string $cert;
|
||||
public int $create;
|
||||
public int $expire;
|
||||
/**
|
||||
* @type Tor\Data\Group
|
||||
*/
|
||||
public array $groups = [];
|
||||
}
|
||||
|
@ -46,4 +46,4 @@
|
||||
</div>
|
||||
<script src="{{ files_url }}/js/window.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -4,14 +4,8 @@
|
||||
<form action="/consent" method="post" style="display: inline;">
|
||||
A service has requested access to your Identity<br><br><label><b>Username:</b></label><br><input name="username" value="{{ username | e }}Hankypants" disabled><br>
|
||||
<textarea rows=10 cols=40 disabled>
|
||||
Common Name:
|
||||
Hankypants
|
||||
Issuer:
|
||||
tor.161.sh
|
||||
Key Fingerprint:
|
||||
CA:3A:85:33:03:1F:39:7B
|
||||
Key ID:
|
||||
A5:CE:37:EA:EB:B0:75:0E</textarea><br>
|
||||
|
||||
</textarea><br>
|
||||
<i>Please verify before continuing.</i><br>
|
||||
<input type="submit" name="confirm" value="Consent" onclick="myagent.play('Print'); myagent.play('GetTechy');"><input type="submit" name="deny" value="Deny Access" onclick="myagent.play('Print'); myagent.play('GetTechy');">
|
||||
</form>
|
||||
|
Loading…
Reference in New Issue
Block a user