rosavox/lib/alveolata/auth/policy.php
2025-05-23 07:33:29 +00:00

298 lines
4.8 KiB
PHP

<?php
namespace alveolata\auth;
// require_once(DIR_ALVEOLATA . '/definitions.php');
require_once(DIR_ALVEOLATA . '/list/functions.php');
require_once(DIR_ALVEOLATA . '/string/functions.php');
require_once(DIR_ALVEOLATA . '/session/session.php');
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class class_exception_policyreport extends \Exception
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
$messages
)
{
parent::__construct(
' | '.join($messages)
);
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
abstract class class_policy
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
)
{
}
/**
* @param record<session_id:string> $environment
* @return list<string>
* @author Christian Fraß <frass@greenscale.de>
*/
abstract public function check(
array $environment
) : array
;
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class class_policy_none extends class_policy
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
)
{
parent::__construct();
}
/**
* @implementation
* @author Christian Fraß <frass@greenscale.de>
*/
public function check(
array $environment
) : array
{
return [];
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class class_policy_conjunction extends class_policy
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
array $subpolicies
)
{
parent::__construct();
$this->subpolicies = $subpolicies;
}
/**
* @implementation
* @author Christian Fraß <frass@greenscale.de>
*/
public function check(
array $environment
) : array
{
return (
\alveolata\list_\reduce(
\alveolata\list_\map(
$this->subpolicies,
function ($policy) use ($environment) {
return $policy->check($environment);
}
),
[],
function ($x, $y) {return \alveolata\list_\concat($x, $y);}
)
);
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class class_policy_disjunction extends class_policy
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
array $subpolicies
)
{
parent::__construct();
$this->subpolicies = $subpolicies;
}
/**
* @implementation
* @author Christian Fraß <frass@greenscale.de>
*/
public function check(
array $environment
) : array
{
$some = \alveolata\list_\some(
$this->subpolicies,
function ($policy) use ($environment) {
return $policy->check($environment);
}
);
return ($some ? [] : ['all alternatives failed']);
}
}
/**
* @author Christian Fraß <frass@greenscale.de>
*/
class class_policy_logged_in extends class_policy
{
/**
* @author Christian Fraß <frass@greenscale.de>
*/
public function __construct(
)
{
parent::__construct();
}
/**
* @implementation
* @author Christian Fraß <frass@greenscale.de>
*/
public function check(
array $environment
) : array
{
if (! array_key_exists('session_id', $environment)) {
return ['session id not set'];
}
else {
if (! \alveolata\session\has($environment['session_id'])) {
return ['no session present'];
}
else {
return [];
}
}
}
}
/**
* @param record<kind:string,parameters:map<strin,any>>
* @return class_policy
* @author Christian Fraß <frass@greenscale.de>
*/
function make(
array $description
) : class_policy
{
switch ($description['kind']) {
default: {
throw (
new \Exception(
\alveolata\string\coin('unhandled policy kind "{{kind}}"', ['kind' => $description['kind']])
)
);
break;
}
case 'none': {
return (
new class_policy_none(
)
);
break;
}
case 'logged_in': {
return (
new class_policy_logged_in(
)
);
break;
}
case 'conjunction': {
return (
new class_policy_conjunction(
\helper\list_\map(
$description['parameters']['sub'] ?? [],
function ($description_) {
return make($description_);
}
)
)
);
break;
}
case 'disjunction': {
return (
new class_policy_disjunction(
\helper\list_\map(
$description['parameters']['sub'] ?? [],
function ($description_) {
return make($description_);
}
)
)
);
break;
}
}
}
/**
* @param class_policy $policy
* @param function<void,type_result> $function
* @param record<session_id:string> $environment
* @return type_result
* @throw \Exception if the policy check fails
* @author Christian Fraß <frass@greenscale.de>
*/
function wrap/*<type_result>*/(
class_policy $policy,
array $environment,
\Closure $function
)/* : type_result*/
{
$messages = $policy->check($environment);
if (count($messages) === 0) {
$result = $function();
return $result;
}
else {
throw (new class_exception_policyreport($messages));
}
}