OTP, Groups, Grants,

This commit is contained in:
root 2023-08-04 21:21:40 +02:00
parent 04f2123bc6
commit 972eeae7d3
17 changed files with 673 additions and 108 deletions

View File

@ -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' ]);
}
}

View File

@ -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;
}
}
?>

View 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;
}
}
?>

View File

@ -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

View 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' ]);
}
}

View File

@ -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

View 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' ]);
}
}

View 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
View 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;
}

View File

@ -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
View File

@ -0,0 +1,10 @@
<?php
namespace Tor\Data;
use Tor\Data\Service;
class Group extends BaseEntity {
public Service $service;
public bool $default;
}

View File

@ -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
View File

@ -0,0 +1,10 @@
<?php
namespace Tor\Data;
enum Status: int
{
case Pending = -1;
case Rejected = 0;
case Granted = 1;
}
?>

View File

@ -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;

View File

@ -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 = [];
}

View File

@ -46,4 +46,4 @@
</div>
<script src="{{ files_url }}/js/window.js"></script>
</body>
</html>
</html>

View File

@ -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>