App/lib_new/71_rights.php

347 lines
8.5 KiB
PHP

<?php
namespace Rights;
class Condition
{
protected $mode = "and";
protected $list = [];
protected $subs = [];
public function __construct($mode = "and") {$this->mode = $mode;}
public function Add($field, $value, $table = null) {
$this->list[] = ["field" => $field, "value" => $value, "table" => $table];
return $this;
}
public function Sub(Condition $condition) {
$this->subs[] = $condition;
return $this;
}
private function checkValue($is, $should) {
if (is_array($is)) {
if (array_key_exists("ID", $is)) {
return $should==$is["ID"];
} else {
foreach ($is as $entry) {
if ($this->checkValue($entry, $should)) {
return true;
} }
return false;
}
return in_array($should, $is);
} else {
return $should==$is;
} }
public function CheckInput($json) {
if ($this->mode=="and") {
foreach ($this->list as $cond) {
if (!$this->checkValue($json[$cond["field"]], $cond["value"])) {
return false;
} }
foreach ($this->subs as $sub) {
if (!$sub->CheckInput($json)) {
return false;
} }
return true;
} else if ($this->mode=="or") {
foreach ($this->list as $cond) {
if ($this->checkValue($json[$cond["field"]], $cond["value"])) {
return true;
} }
foreach ($this->subs as $sub) {
if ($sub->CheckInput($json)) {
return true;
} }
return false;
}
return false; // Failsafe if mode was misconfigured
}
public function GetFilterCondition() {
$conditionList = new \Filter\ConditionList($this->mode);
foreach ($this->list as $cond) {
$conditionList->Add(new \Filter\Condition($cond["field"], "=", $cond["value"], $cond["table"]));
}
foreach ($this->subs as $sub) {
$conditionList->Add($sub->GetFilterCondition());
}
return $conditionList;
}
public function Export() {
if (sizeof($this->list) + sizeof($this->subs) == 1) {
return empty($this->list) ? $this->subs[0]->Export() : $this->list[0];
}
$ret = [
"mode" => $this->mode,
"conditions" => $this->list
];
foreach ($this->subs as &$condition) {
$ret["conditions"][] = $condition->Export();
}
return $ret;
}
public static function Make($mode = "and") {
return (new self($mode));
}
}
class Restriction
{
protected $condition = null;
protected $limits = [];
public function AddCondition(Condition $condition) {
if (is_null($this->condition)) {
$this->condition = Condition::Make("or");
}
$this->condition->Sub($condition);
return $this;
}
public function AddLimits($fields) {
if (is_array($fields)) {
foreach ($fields as $field) {
$this->limits[] = $field;
}
} else {
$this->limits[] = $fields;
}
return $this;
}
public function CheckInput($id = null, $json = []) {
// Check limits
if (!empty($json) && !empty($this->limits)) {
foreach ($json as $field => $value) {
if (!in_array($field, $this->limits)) {
return false;
} } }
// Check condition
return is_null($this->condition) || $this->condition->CheckInput(array_merge(["ID" => $id], $json));
}
public function GetFilterCondition() {
return is_null($this->condition) ? null : $this->condition->GetFilterCondition();
}
public function Export() {
return [
"limits" => $this->limits,
"condition" => is_null($this->condition) ? null : $this->condition->Export()
];
}
}
abstract class Right
{
protected $requiredRight = null;
protected $restriction = null;
protected $allow = [];
public function __construct($requiredRight = null) {
$this->requiredRight = $requiredRight;
}
public function IsApplicable() {
return is_null($this->requiredRight) || \Login::HasRight($this->requiredRight);
}
public function IsAdmin() {
return sizeof($this->allow)==6 && is_null($this->restriction);
}
public function Require(Condition $condition) {
if (is_null($this->restriction)) {
$this->restriction = new Restriction();
}
$this->restriction->AddCondition($condition);
return $this;
}
public function Limit($fields) {
if (is_null($this->restriction)) {
$this->restriction = new Restriction();
}
$this->restriction->AddLimits($fields);
return $this;
}
public function GetAllowed() {
return $this->allow;
}
public function GetRestriction() {
return $this->restriction;
}
public static function Make($requiredRight = null) {
$class = get_called_class();
return (new $class($requiredRight));
}
}
class Main extends Right
{
public static function AdminAllow() {
return ["get", "update", "insert", "remove", "upload", "erase"];
}
public function Allow($allow = "readonly") {
if (is_array($allow)) {
$this->allow = array_intersect($allow, self::AdminAllow());
} else {
switch ($allow) {
case "admin":
$this->allow = self::AdminAllow();
break;
case "nodelete":
$this->allow = ["get", "update", "insert", "upload"];
break;
case "justdata":
$this->allow = ["get", "update", "insert", "remove"];
break;
case "update":
$this->allow = ["get", "update"];
break;
case "readonly":
$this->allow = ["get"];
break;
default:
$this->allow = [];
} }
return $this;
}
}
class Link extends Right
{
public static function AdminAllow() {
return ["set", "update", "unset"];
}
public function Allow($allow = "readonly") {
if (is_array($allow)) {
$this->allow = array_intersect($allow, self::AdminAllow());
} else {
switch ($allow) {
case "admin":
$this->allow = self::AdminAllow();
break;
case "nodelete":
$this->allow = ["set", "update"];
break;
case "justadd":
$this->allow = ["set"];
break;
case "update":
$this->allow = ["update"];
break;
default:
$this->allow = [];
} }
return $this;
}
}
abstract class Rights {
protected function __construct($adminAllow) {
$this->actions = array_fill_keys($adminAllow, false);
}
public function Add($right) {
// If user already has admin rights or does not posess this right, skip
if ($right->IsApplicable()) {
$restriction = $right->GetRestriction();
// Parse the right for every action
foreach ($right->GetAllowed() as $action) {
if (is_null($restriction)) {
// No restriction in the added right, so just overwrite current restrictions
$this->actions[$action] = true;
} else if (is_array($this->actions[$action])) {
// Restrictions applied before (or nothing was allowed) and in the new one, so add new restrictions
$this->actions[$action][] = $restriction;
} else if ($this->actions[$action]===false) {
// Not allowed before, but now restrictions apply
$this->actions[$action] = [$restriction];
} } }
return $this;
}
public function CheckInput($action, $id = null, $json = []) {
if (!is_array($this->actions[$action])) {
return $this->actions[$action];
}
// At this point, some but not everything is allowed for this user and this action, so check more carefully
foreach ($this->actions[$action] as $restriction) {
if ($restriction->CheckInput($id, $json)) {
return true;
} }
return false;
}
public function RestrictFilter() {
if (is_array($this->actions["get"])) {
$restrictions = new \Filter\ConditionList("or");
foreach ($this->actions["get"] as $restriction) {
$filterCondition = $restriction->GetFilterCondition();
if (!is_null($filterCondition)) {
$restrictions->Add($filterCondition);
}
}
\Filter\Filter::Restrict($restrictions);
return true;
} else {
return $this->actions["get"];
} }
public function Export() {
$ret = [];
foreach ($this->actions as $action => $restrictions) {
if (is_array($restrictions)) {
$ret[$action] = [];
foreach ($restrictions as $restriction) {
$ret[$action][] = $restriction->Export();
}
} else {
$ret[$action] = $restrictions;
} }
return $ret;
}
}
class MainRights extends Rights
{
// false == not allowed
// array with refs to restrictions [a ... n] == restrictions apply
// true == allowed without restrictions
protected $actions = [];
public static function Make() {
return (new self(Main::AdminAllow()))->Add(Main::Make("ADMIN")->Allow("admin"));
}
}
class LinkRights extends Rights
{
// false == not allowed
// array with refs to restrictions [a ... n] == restrictions apply
// true == allowed without restrictions
protected $actions = [];
public static function Make() {
return (new self(Link::AdminAllow()))->Add(Link::Make("ADMIN")->Allow("admin"));
}
}