diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..afd3e18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +upl/ +qr/ +conf/*.php +var/*.php diff --git a/bin/init.php b/bin/init.php index 6f12814..f8ecf56 100755 --- a/bin/init.php +++ b/bin/init.php @@ -10,14 +10,14 @@ foreach ($files["ext"] as $val) { //Include libraries (excl. hidden [.file]) $files["lib"] = scandir("lib"); foreach ($files["lib"] as $val) { - if (substr($val,0,1)!= "." && $val!="99_manager_new.php") { + if (substr($val,0,1)!= ".") { include "lib/".$val; } } //Import configuration files (excl. hidden [.file]) $files["var"] = scandir("var"); foreach ($files["var"] as $val) { - if (substr($val,0,1)!= ".") { + if (substr($val,0,1)!= "." && ".php"==substr($val,strlen($val)-4,4)) { include "var/".$val; } } diff --git a/bin/init_new.php b/bin/init_new.php index 89280c9..aea9e4e 100644 --- a/bin/init_new.php +++ b/bin/init_new.php @@ -17,6 +17,6 @@ foreach ($files["lib"] as $val) { //Import configuration files (excl. hidden [.file]) $files["conf"] = scandir("conf"); foreach ($files["conf"] as $val) { - if (substr($val,0,1)!= ".") { + if (substr($val,0,1)!= "." && ".php"==substr($val,strlen($val)-4,4)) { include "conf/".$val; } } diff --git a/conf/30_login.php b/conf/30_login.php index ed2ceba..cd8c2b2 100644 --- a/conf/30_login.php +++ b/conf/30_login.php @@ -6,7 +6,7 @@ define( "JWT_KEY", "v9upLZkm7uVGH9zqEGnp4sZAqeJX33vygYBTKEGdVqtsKeEKaSGp2KBnZmcVNPDb" ."3BkXGwdp9GMDwjaqp6BqfVNRYQf9X5PC8BP74UZPKGPujCeAsayMRpLv2GPBUgXz" - ."TVY36KcpYy77SPUmZ7zY2q7ZTFEvM73jMBufdUV8fcEDp4eTQDqMpvp8dhkDxZPj" + ."ZVY36KcpYy77SPUmZ7zY2q7ZTFEvM73jMBufdUV8fcEDp4eTQDqMpvp8dhkDxZPj" ."wVYtkHxLtkTHVa9DMm2pWmXqhsjJckgPMxdRsgngCSv8H8BBjXFkG2dn7ndJeApF" ); define("JWT_VALID_TIME", 7 * 24 * 60 * 60); diff --git a/lib_new/70_routes.php b/lib_new/70_routes.php index 61b3954..b852419 100644 --- a/lib_new/70_routes.php +++ b/lib_new/70_routes.php @@ -7,74 +7,82 @@ namespace Routes; /**************************************************/ abstract class Route { - public function __construct() {} + protected $resourceClass; + + public function __construct() { + $this->resourceClass = ("\\Resources\\".(get_called_class()::$resource)); + } public function Answer() { - $subroute = \Request::Subroute(); - if (is_null($subroute)) { - switch (\Request::Method()) { - case "OPTIONS": - return $this->info(); - case "HEAD": - $id = \Request::ID(); - return $this->restrictFilter($id) && $this->head($id); - case "GET": - $id = \Request::ID(); - if (is_null($id)) { - return $this->restrictFilter() && $this->head() && $this->getAll(); - } else { - $json = []; - return $this->head($id) && $this->getOne($id, $json) && $this->checkOutput($id, $json); - } - case "POST": - $json = \Request::Input("content"); - return $this->checkInput("insert", null, $json) && $this->insert($json) && $this->head(); - case "PATCH": - $json = \Request::Input("content"); - $id = \Request::ID(); - return $this->checkInput("update", $id, $json) && $this->update($id, $json) && $this->head(); - case "DELETE": - $id = \Request::ID(); - return $this->checkInput("remove", $id) && $this->remove($id) && $this->head(); - default: - \Response::Get()->NotImplemented(); - return false; - } - } else { - $id = \Request::ID(); - return $this->answerSub($subroute, $id) && $this->head($id) && $this->get($id) && $this->checkOutput($id, ); - } } + try { + $subroute = \Request::Subroute(); + if (is_null($subroute)) { + switch (\Request::Method()) { + case "OPTIONS": + return $this->info(); + case "HEAD": + $id = \Request::ID(); + return $this->restrictFilter($id) && $this->head($id); + case "GET": + $id = \Request::ID(); + if (is_null($id)) { + return $this->restrictFilter() && $this->getAll() && $this->head(); + } else { + $json = []; + return $this->head($id) && $this->getOne($id, $json) && $this->checkOutput($id, $json); + } + case "POST": + $json = \Request::Input("content"); + return $this->checkInput("insert", null, $json) && $this->insert($json) && $this->head(); + case "PATCH": + $json = \Request::Input("content"); + $id = \Request::ID(); + return $this->checkInput("update", $id, $json) && $this->update($id, $json) && $this->head(); + case "DELETE": + $id = \Request::ID(); + return $this->checkInput("remove", $id) && $this->remove($id) && $this->head(); + default: + \Response::Get()->NotImplemented(); + return false; + } + } else { + $id = \Request::ID(); + $json = []; + return $this->answerSub($subroute, $id) && $this->getOne($id, $json) && $this->head($id) && $this->checkOutput($id, $json); + } } + catch (ResponseException $e) { + \Response::Get()->HandleException($e); + return; + } + \Response::Get()->Good(); + } abstract public static function Rights(); protected function info() { - $class = get_called_class(); - \Response::Get()->Good()->Json("rights", $class::Rights()->Export()); + \Response::Get()->Good()->Json("rights", get_called_class()::Rights()); return true; } protected function answerSub($subroute, $id) { - $class = "\\Resources\\".$this->resource; - $answerFnc = "answer_".$class::Get()->Table()."_".$subroute; + $answerFnc = "answer_".$this->resourceClass::Get()->Table()."_".$subroute; if (method_exists($this, $answerFnc)) { $subid = \Request::SubID(); return $this->$answerFnc($id, $subid); - } else if (method_exists($this, "answer_Files") && $class::Get()->HasFile($subroute)) { - return $this->answer_Files($this->resource, $id, $subroute); + } else if (method_exists($this, "answer_Files") && $this->resourceClass::Get()->HasFile($subroute)) { + return $this->answer_Files(get_called_class()::$resource, $id, $subroute); } \Response::Get()->SubRouteNotExisting($subroute); return false; } protected function head($id = null) { - $class = "\\Resources\\".$this->resource; - \Response::Get()->Good()->Etag($class::Get()->Checksum($id)); + \Response::Get()->Good()->Etag($this->resourceClass::Get()->Checksum($id)); return true; // Needed for &&-chain-call } protected function getOne($id, &$json) { - $class = "\\Resources\\".$this->resource; - $resource = $class::Get()->Ref($id); + $resource = $this->resourceClass::Get()->Ref($id); if ($resource->Load()) { $json = $resource->Json(); return true; @@ -85,8 +93,7 @@ abstract class Route { } protected function getAll() { - $class = "\\Resources\\".$this->resource; - $list = $class::Get()->RefAll(); + $list = $this->resourceClass::Get()->RefAll(); $json = []; foreach ($list as $resource) { $json[] = $resource->Json(); @@ -98,38 +105,35 @@ abstract class Route { } protected function insert($json) { - $class = "\\Resources\\".$this->resource; $newid = null; - if ($class::Get()->Insert($json, $newid)) { - \Response::Get()->Inserted($this->resource)->Content($class::Get()->Ref($newid)->Json()); + if ($this->resourceClass::Get()->Insert($json, $newid)) { + \Response::Get()->Inserted(get_called_class()::$resource)->Content($this->resourceClass::Get()->Ref($newid)->Json()); return true; } return false; } protected function update($id, $json) { - $class = "\\Resources\\".$this->resource; - $resource = $class::Get()->Ref($id); + $resource = $this->resourceClass::Get()->Ref($id); if ($resource->Load()) { $resource->Patch($json); if ($resource->Store()) { - \Response::Get()->Updated($this->resource)->Content($resource->Json()); + \Response::Get()->Updated(get_called_class()::$resource)->Content($resource->Json()); return true; } } return false; } protected function remove($id) { - $class = "\\Resources\\".$this->resource; - if ($class::Get()->Remove($id)) { - \Response::Get()->Deleted($this->resource); + if ($this->resourceClass::Get()->Remove($id)) { + \Response::Get()->Deleted(get_called_class()::$resource); return true; } return false; } protected function restrictFilter() { - if (get_called_class()::Rights()->RestrictFilter()) { + if ($this->resourceClass::Rights()->RestrictFilter()) { return true; } \Response::Get()->IllegalQuery(); @@ -137,7 +141,7 @@ abstract class Route { } protected function checkOutput($id, $json) { - if (get_called_class()::Rights()->CheckInput("get", $id, $json)) { + if (($this->resourceClass)::Rights()->CheckInput("get", $id, $json)) { \Response::Get()->Good()->Content($json); return true; } @@ -152,7 +156,7 @@ abstract class Route { } else if ((is_null($json) || empty($json)) && in_array($action, ["insert", "update"])) { \Response::Get()->MissingContent(); return false; - } else if (!get_called_class()::Rights()->CheckInput($action, $id, $json)) { + } else if (!($this->resourceClass)::Rights()->CheckInput($action, $id, $json)) { \Response::Get()->IllegalInput(); return false; } diff --git a/lib_new/71_rights.php b/lib_new/71_rights.php index f2cea9a..7573abe 100644 --- a/lib_new/71_rights.php +++ b/lib_new/71_rights.php @@ -1,8 +1,6 @@ -condition)) { - $this->condition = \Condition::Make("or"); + $this->condition = Condition::Make("or"); } $this->condition->Sub($condition); return $this; @@ -142,7 +140,7 @@ class Restriction } } -class Right +abstract class Right { protected $requiredRight = null; protected $restriction = null; @@ -160,32 +158,9 @@ class Right return sizeof($this->allow)==6 && is_null($this->restriction); } - public function Allow($allow = "readonly") { - switch ($allow) { - case "admin": - $this->allow = ["get", "update", "insert", "remove", "upload", "erase"]; - 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; - } - - public function Require(\Condition $condition) { + public function Require(Condition $condition) { if (is_null($this->restriction)) { - $this->restriction = new \Restriction(); + $this->restriction = new Restriction(); } $this->restriction->AddCondition($condition); return $this; @@ -193,7 +168,7 @@ class Right public function Limit($fields) { if (is_null($this->restriction)) { - $this->restriction = new \Restriction(); + $this->restriction = new Restriction(); } $this->restriction->AddLimits($fields); return $this; @@ -208,32 +183,78 @@ class Right } public static function Make($requiredRight = null) { - return (new self($requiredRight)); - } - - public static function AllowSelf() { - return self::Make()->Allow("readonly")->Require(\Condition::Make()->Add("ID", \Login::ID())); - } - - public static function AllowUpdateSelf() { - return self::Make()->Allow("update")->Require(\Condition::Make()->Add("ID", \Login::ID())); + $class = get_called_class(); + return (new $class($requiredRight)); } } -class Rights { - // false == not allowed - // array with refs to restrictions [a ... n] == restrictions apply - // true == allowed without restrictions - protected $actions = [ - "get" => false, - "update" => false, - "insert" => false, - "remove" => false, - "upload" => false, - "erase" => false - ]; +class Main extends Right +{ + public static function AdminAllow() { + return ["get", "update", "insert", "remove", "upload", "erase"]; + } - public function __construct() {} + 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 @@ -296,8 +317,31 @@ class Rights { } } 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())->Add(\Right::Make("ADMIN")->Allow("admin")); + 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")); + } +} \ No newline at end of file diff --git a/lib_new/99_manager.php b/lib_new/99_manager.php index f4ace22..92748a1 100644 --- a/lib_new/99_manager.php +++ b/lib_new/99_manager.php @@ -34,8 +34,8 @@ class Manager } else { // Include routes. Needed also for /-Request - foreach (self::$routes as $route => $filename) { - $file = "routes/".str_replace(["ä", "ö", "ü", "ß"], ["ae", "oe", "ue", "ss"], $filename).".php"; + foreach (self::$routes as $route) { + $file = "routes/".str_replace(["ä", "ö", "ü", "ß"], ["ae", "oe", "ue", "ss"], $route).".php"; if (file_exists($file)) { include $file; } } @@ -43,7 +43,7 @@ class Manager if (Request::IsRoot()) { // POST (Login) or GET/OPTIONS... i dont care, all will result in the same $access = []; - foreach (self::$routes as $route => $filename) { + foreach (self::$routes as $route) { $class = "\\Routes\\".$route; $access[$route] = $class::Rights(); } diff --git a/links/Fahrzeuge_Einweisungen.php b/links/Fahrzeuge_Einweisungen.php index 489bd2c..136beff 100644 --- a/links/Fahrzeuge_Einweisungen.php +++ b/links/Fahrzeuge_Einweisungen.php @@ -14,4 +14,15 @@ class Fahrzeuge_Einweisungen extends \Links\Link "fields" => [], "keys" => [] ]; + + protected static $rights = null; + + public static function Rights() { + if (is_null(self::$rights)) { + self::$rights = \Rights\LinkRights::Make() + ->Add(\Rights\Link::Make("DARF_PERSONAL_VERWALTEN")->Allow("admin")) + ->Add(\Rights\Link::Make("DARF_FAHRZEUGEINWEISUNGEN_VERWALTEN")->Allow("admin")); + } + return self::$rights; + } } diff --git a/resources/Fahrzeuge/Fahrzeug.php b/resources/Fahrzeuge/Fahrzeug.php index 6d2491a..21b0ab7 100644 --- a/resources/Fahrzeuge/Fahrzeug.php +++ b/resources/Fahrzeuge/Fahrzeug.php @@ -20,4 +20,15 @@ class Fahrzeug extends \Resources\Handler "Einweisungen" => ["linkClass" => "Fahrzeuge_Einweisungen"], ] ]; + + protected static $rights = null; + + public static function Rights() { + if (is_null(self::$rights)) { + self::$rights = \Rights\MainRights::Make() + ->Add(\Rights\Main::Make()->Allow("readonly")) // Jeder darf alle Fahrzeuge SEHEN + ->Add(\Rights\Main::Make("DARF_FAHRZEUGE_VERWALTEN")->Allow("admin")); + } + return self::$rights; + } } \ No newline at end of file diff --git a/resources/Personal/Personal.php b/resources/Personal/Personal.php index 4e8ec41..90bfb13 100644 --- a/resources/Personal/Personal.php +++ b/resources/Personal/Personal.php @@ -40,4 +40,27 @@ class Personal extends \Resources\Handler "Bildadresse" => "Nopic.svg", ] ]; -} \ No newline at end of file + + protected static $rights = null; + + public static function Rights() { + if (is_null(self::$rights)) { + self::$rights = \Rights\MainRights::Make() + ->Add(\Rights\Main::Make("DARF_PERSONAL_VERWALTEN")->Allow("admin")) + // Jeder darf sich selbst sehen + ->Add(\Rights\Main::Make()->Allow("readonly")->Require(\Rights\Condition::Make()->Add("ID", \Login::ID()))) + // Jeder darf seine Telefonnummer und E-Mail-Adresse ändern + ->Add(\Rights\Main::Make() + ->Allow(["update"]) + ->Require(\Rights\Condition::Make()->Add("ID", \Login::ID())) + ->Limit(["Telefon", "EMail"])) + ->Add(\Rights\Main::Make("DARF_KRAFTFAHRER_SEHEN") + ->Allow("readonly") + ->Require(\Rights\Condition::Make()->Add("Lehrgänge", 5, \Links\Personal_Lehrgänge::Get()->Short()))) + ->Add(\Rights\Main::Make("DARF_AGTS_SEHEN") + ->Allow("readonly") + ->Require(\Rights\Condition::Make()->Add("Lehrgänge", 2, \Links\Personal_Lehrgänge::Get()->Short()))); + } + return self::$rights; + } +} diff --git a/roadmap.txt b/roadmap.txt index 81693df..c3185ed 100755 --- a/roadmap.txt +++ b/roadmap.txt @@ -65,6 +65,9 @@ Konzeption Feuerwehr-App https://github.com/nimiq/qr-scanner - Client 2 / Server 3 - - Framework für Mobile- und Webintegration (Flutter?) - Entkopplung von Templates und Pages - - Golang statt PHP + - Objektifizierung des Backends + +- Client 3 / Server 4 + - Framework für Mobile- und Webintegration (Flutter?) + - Golang/Rust/C++ statt PHP diff --git a/routes/Personal.php b/routes/Personal.php index 85915ba..f0b3d41 100644 --- a/routes/Personal.php +++ b/routes/Personal.php @@ -3,7 +3,6 @@ namespace Routes; require_once("resources/Personal/Personal.php"); -require_once("links/Personal_Lehrgaenge.php"); require_once("subroutes/Fahrzeuge_Personal.php"); require_once("subroutes/Personal_Abteilungen.php"); require_once("subroutes/Personal_Gruppen.php"); @@ -20,19 +19,16 @@ class Personal extends Route use \Subroutes\Personal_Verwalter; use \subroutes\Files; - protected $resource = "Personal\\Personal"; - - protected static $rights = null; + protected static $resource = "Personal\\Personal"; public static function Rights() { - if (is_null(self::$rights)) { - self::$rights = \Rights::Make() - ->Add(\Right::Make("DARF_PERSONAL_VERWALTEN")->Allow("admin")) - ->Add(\Right::AllowSelf()) - ->Add(\Right::AllowUpdateSelf()->Limit(["Telefon", "EMail"])) - ->Add(\Right::Make("DARF_KRAFTFAHRER_SEHEN")->Allow("readonly")->Require(\Condition::Make()->Add("Lehrgänge", 5, \Links\Personal_Lehrgänge::Get()->Short()))) - ->Add(\Right::Make("DARF_AGTS_SEHEN")->Allow("readonly")->Require(\Condition::Make()->Add("Lehrgänge", 2, \Links\Personal_Lehrgänge::Get()->Short()))); - } - return self::$rights; + return [ + "" => ("\\Resources\\".self::$resource)::Rights()->Export(), + "Fahrzeuge" => \Links\Fahrzeuge_Einweisungen::Rights()->Export(), +// "Abteilungen" => \Links\Personal_Abteilungen::Rights()->Export(), +// "Gruppen" => \Links\Personal_Gruppen::Rights()->Export(), +// "Lehrgänge" => \Links\Personal_Lehrgänge::Rights()->Export(), +// "Verwalter" => \Links\Personal_Verwalter::Rights()->Export() + ]; } } diff --git a/var/10_conf.php b/var/10_conf.php index dc1eb18..0815960 100755 --- a/var/10_conf.php +++ b/var/10_conf.php @@ -10,7 +10,7 @@ define( "JWT_KEY", "v9upLZkm7uVGH9zqEGnp4sZAqeJX33vygYBTKEGdVqtsKeEKaSGp2KBnZmcVNPDb" ."3BkXGwdp9GMDwjaqp6BqfVNRYQf9X5PC8BP74UZPKGPujCeAsayMRpLv2GPBUgXz" - ."TVY36KcpYy77SPUmZ7zY2q7ZTFEvM73jMBufdUV8fcEDp4eTQDqMpvp8dhkDxZPj" + ."ZVY36KcpYy77SPUmZ7zY2q7ZTFEvM73jMBufdUV8fcEDp4eTQDqMpvp8dhkDxZPj" ."wVYtkHxLtkTHVa9DMm2pWmXqhsjJckgPMxdRsgngCSv8H8BBjXFkG2dn7ndJeApF" ); define("JWT_VALID_TIME", 7 * 24 * 60 * 60); @@ -23,4 +23,4 @@ define( // Date and time locale setlocale(LC_TIME, "de_DE.UTF-8"); setlocale(LC_ALL,"de_DE.UTF8"); -$formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::NONE); \ No newline at end of file +$formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::NONE);