Berechtigungen der Nutzer sind eine einfache und effektive Möglichkeit, zu verhindern, dass jeder Nutzer Zugriff auf jeden Raum hat. Damit können Nutzer, die nicht über die nötigen Berechtigungen verfügen, nicht versehentlich Geräte löschen oder verändern oder sogar absichtlich Geräte in Räumen schalten, zu denen sie eigentlich keinen Zugriff haben sollten. Um diesem Tutorial folgen zu können, solltest du dir alle Tutorials aus der Kategorie "Smarthome-Server" durchlesen und sie nachmachen.
Dieses Tutorial als Video
Berechtigungen in Datenbank einfügen
Als erstes rufst du in deinem Browser die Adresse "http://[IP DES PI]/database/phpliteadmin.php" auf und gibst gegebenenfalls das Passwort ein ("admin"). Um die Berechtigungen nun in der Datenbank abspeichern zu können klickst du in der Übersicht bei der Tabelle "userdata" auf den Punkt "structure". Anschließend klickst du unten bei "Add 1 field at end of table" auf "GO", erstellst ein neues Feld mit dem Namen "PERMISSIONS" und dem Datentyp "TEXT", wie im Bild zu sehen und klickst auf "GO" um den Vorgang abzuschließen. In dem Feld "PERMISSIONS" werden die Berechtigungen im JSON-Format abgespeichert.Funktionen zum Abfragen und Setzen von Berechtigungen
Um Berechtigungen abfragen und setzen zu können, muss noch ein Skript erstellt werden, das diese Funktionen zur Verfügung stellt. Dazu verbindest du dich per FileZilla mit deinem Pi und erstellst im Serverhauptverzeichnis eine neue Datei namens "permissions.php". Diese Datei öffnest du jetzt und fügst dort den folgenden Quellcode ein.<?php
function getPermissions($username, $db){
//Berechtigungen des Nutzers aus der Datenbank laden
$results = $db->prepare("SELECT * FROM 'userdata' WHERE USERNAME == :username");
$results->execute(array('username' => $username));
//Abfrageergebnisse speichern
if($result = $results->fetch(PDO::FETCH_ASSOC)){
$permissions = $result['PERMISSIONS'];
}
return $permissions;
}
function setPermissions($username, $permissions, $db){
if(!hasPermission("admin", $db)){
return "nopermission";
}
//Berechtigungen des Nutzers aktualisieren
$results = $db->prepare("UPDATE 'userdata' SET PERMISSIONS = :permissions WHERE USERNAME == :username");
$results->execute(array('username' => $username, 'permissions' => $permissions));
return "ok";
}
?>
Die Schnittstelle bearbeiten
Damit alle Skripte der Schnittstelle prüfen können, ob ein Nutzer die nötigen Berechtigungen zur Ausführung der gewünschten Aktion besitzt, muss die Datei "api.php" geöffnet und ein wenig angepasst werden. Dazu wurde die Datei "permissions.php" inkludiert (Zeile 20), 2 neue Cases zum switch-Block hinzugefügt (Zeilen 48-53) und eine Funktion erstellt, um zu prüfen, ob der übergebene Nutzer Zugriff auf den übergebenen Raum hat (Zeilen 92-97).<?php
//Datenbankverbindung herstellen
$SQLITEdb = "database/data.sqlite";
$db = new PDO("sqlite:".$SQLITEdb);
//Funksteckdosen
include "getModes.php";
include "setModes.php";
//Andere
include "getRooms.php";
include "getSensorData.php";
include "getSystemInfo.php";
include "getRoomData.php";
include "getGraphData.php";
include "events.php";
//Diese Zeile wurde hinzugefügt
include "permissions.php";
//Szenen
include "scenes/getScenes.php";
include "scenes/runScene.php";
include "scenes/createScene.php";
$validUser = validateUser($_POST['username'], $_POST['password'], $db);
if($validUser){
switch($_POST['action']){
case "getrooms":
echo getRooms($db);
break;
case "getevents":
echo getEvents($_POST['username'], $_POST['type'], $_POST['limit'], $_POST['offset'], $db);
break;
case "geteventtypes":
echo getEventTypes($db);
break;
case "addevent":
echo addEvent($_POST['type'], $_POST['text'], $db);
break;
case "getunseenevents":
echo getUnseenEvents($_POST['username'], $db);
break;
//Diese Zeilen wurden hinzugefügt
case "getpermissions":
echo getPermissions($_POST['user'], $db);
break;
case "setpermissions":
echo setPermissions($_POST['user'], $_POST['permissions'], $db);
break;
case "getgraphdata":
echo getGraphData($_POST['room'], $_POST['value'], $_POST['von'], $_POST['bis'], $db);
break;
case "getroomdata":
echo getRoomData($_POST['room'], $db);
break;
case "getmodes":
echo getModes($_POST['room'], $_POST['device'], $db);
break;
case "setmodes":
echo setModes($_POST['room'], $_POST['device'], $_POST['zustand'], $db);
break;
case "getsensordata":
echo getSensorData($_POST['room'], $_POST['value'], $_POST['showeinheit'], $db);
break;
case "runscene":
echo runScene($_POST['room'], $_POST['name'], $db);
break;
case "createscene":
echo createScene($_POST['devices'], $_POST['rooms'], $_POST['types'], $_POST['values'], $_POST['conditions'], $_POST['room'], $_POST['name'], $db);
break;
case "getscenes":
echo getScenes($_POST['room'], $db);
break;
case "getsysteminfo":
echo getSystemInfo();
break;
}
}
function validateUser($username, $password, $db){
//wird noch implementiert
return true;
}
//Diese Funktion wurde hinzugefügt
function hasPermission($action, $db){
$permissions = getPermissions($_POST['username'], $db);
$permissions = json_decode($permissions, true)['permissions'];
return (in_array($action, $permissions) || in_array("admin", $permissions));
}
?>
Skripte anpassen
Nun müssen natürlich auch alle bisher angelegten Skripte angepasst werden, um sicherzustellen, dass jeder Nutzer nun Aktionen ausführen kann, zu denen er auch berechtigt ist. Dazu werden in alle nötigen Skripte ein paar wenige Zeilen Code eingefügt.setModes.php
Öffne mit FileZilla die Datei "setModes.php" und füge die neuen Codezeilen (Zeilen 6-8), damit nur berechtigte Nutzer bestimmte Steckdosen schalten können.<?php
function setModes($room, $device, $zustand, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
//Hauscode und Steckdosennummer aus Datenbank laden
$query = $db->prepare("SELECT * FROM 'funksteckdosen' WHERE ROOM == :room AND DEVICE == :device");
$query->execute(array('room' => $room, 'device' => $device));
//Abfrageergebnisse speichern
if($result = $query->fetch(PDO::FETCH_ASSOC)){
$hauscode = $result['HAUSCODE'];
$steckdosennummer = $result['STECKDOSENNUMMER'];
}
//Schaltbefehl für Steckdosen
shell_exec("/usr/local/bin/send ".$hauscode." ".$steckdosennummer." ".$zustand);
//Status der geschalteten Steckdose aktualisieren
$query = $db->prepare("UPDATE 'funksteckdosen' SET 'ZUSTAND' = :zustand WHERE ROOM == :room AND DEVICE == :device");
$query->execute(array('zustand' => $zustand, 'room' => $room, 'device' => $device));
//Rückgabe
return "SET ".$device." IN ".$room." TO ".$zustand;
}
?>
getModes.php
Als nächstes wird die Datei "getModes.php" bearbeitet, um auch den Zugriff auf die Abfrage von Schalterstellungen einzuschränken. Füge dazu die Zeilen 6-8 hinzu, speichere das Skript und lade die Änderungen hoch.<?php
function getModes($room, $device, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
if($device==""){
//Daten des gesuchten Raumes aus der Datenbank laden
$results = $db->prepare("SELECT * FROM 'funksteckdosen' WHERE ROOM == :room");
$results->execute(array('room' => $room));
$modi = array();
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
$mode_item = array('device' => $row['DEVICE'], 'mode' => $row['ZUSTAND'], 'icon' => $row['ICON'], 'name' => $row['NAME']);
array_push($modi, $mode_item);
}
header('Content-type: application/json');
return json_encode(array('modi' => $modi));
}
else{
//Schaltzustand aus Datenbank laden
$results = $db->prepare("SELECT * FROM 'funksteckdosen' WHERE ROOM == :room AND DEVICE == :device");
$results->execute(array('room' => $room, 'device' => $device));
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
return $row['ZUSTAND'];
}
}
}
?>
getRoomData.php
Auch der Datei "getRoomData.php" werden die Zeilen 6-8 hinzugefügt, damit nur berechtigte Nutzer die Raumdaten abfragen können. Danach wird das Skript, wie immer, gespeichert und die Änderungen hochgeladen.<?php
function getRoomData($room, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
$roomdata = array();
$switchdata = json_decode(getModes($room, "", $db), true);
foreach($switchdata['modi'] as $switch){
if($switch['mode'] === "1"){
$mode = true;
}
else{
$mode = false;
}
array_push($roomdata, array('name' => $switch['name'], 'device' => $switch['device'], 'icon' => $switch['icon'], 'type' => "switch", 'value' => ($mode) ? 'true' : 'false'));
}
$sensordata = json_decode(getSensorData($room, "", "", $db), true);
foreach($sensordata['values'][0]['value_array'] as $sensor){
array_push($roomdata, array('name' => $sensor['shortform'], 'device' => $sensor['sensorart'], 'icon' => $sensor['sensorart'], 'type' => "value", 'value' => $sensor['wert']));
}
$scenedata = json_decode(getScenes($room, $db), true);
if(sizeOf($scenedata['scenes']) > 0){
array_push($roomdata, array('name' => "Szenen", 'device' => "scenes", 'icon' => "scenes", 'type' => "scenes", 'value' => ""));
}
//Heizungs-Item implementieren
return json_encode(array('roomdata' => $roomdata));
}
?>
getRooms.php
In der Datei "getRooms.php" funktioniert die Berechtigungsabfrage ein wenig anders, als bisher. Hier wird nämlich in einer Schleife für jeden Raum geprüft, ob der aktuelle Nutzer die nötigen Berechtigungen besitzt. Falls ja, wird der Raum in das Ausgabearray geschrieben, falls nicht jedoch nicht. Dazu werden die Zeilen 15-17 ins Skript eingefügt. Anschließend wird die Datei gespeichert und die Änderungen hochgeladen<?php
function getRooms($db){
//Räume laden
$results = $db->prepare("SELECT * FROM 'ROOMS'");
$results->execute();
$rooms = array();
//JSON-Objekt erstellen und füllen
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($row['LOCATION'], $db)){
continue;
}
$room_item = array('name' => $row['NAME'], 'location' => $row['LOCATION']);
array_push($rooms, $room_item);
}
//JSON-Objekt ausgeben
header('Content-type: application/json');
return json_encode(array('rooms' => $rooms));
}
?>
getGraphData.php
In der Datei "getGraphData.php" werden wie vorhin die Zeilen 6-8 hinzugefügt, um das Skript bei fehlenden Berechtigungen sofort mit der Nachricht "nopermission" abzubrechen. Danach wird das Skript gespeichert und auf den Server hochgeladen.<?php
function getGraphData($room, $value, $von, $bis, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
if($von == $bis){
//Datum formattieren
$datum = date("d.m.y", strtotime($von));
return getDayData($room, $value, $datum, $db);
}
else{
return getDayMinMax($room, $value, $von, $bis, $db);
}
}
function getDayMinMax($raum, $value, $start, $ende, $db){
$dayseconds = 60 * 60 * 24;
$dateS = strtotime($start);
$dateE = strtotime($ende);
$values = array();
while($dateS!=($dateE+$dayseconds)){
$current_values = getDayValues($raum, $value, date("d.m.y", $dateS), $db);
if(!empty($current_values))
array_push($values, $current_values);
//Einen Tag vorrücken
$dateS += $dayseconds;
}
//Entsprechende Einheit aus Datenbank laden
$query = $db->query("SELECT * FROM 'ZWAVE_SENSOREN' WHERE RAUM == :room AND SENSORART == :value");
$query->execute(array('room' => $raum, 'value' => $value));
if($result = $query->fetch(PDO::FETCH_ASSOC)){
$einheit = $result['EINHEIT'];
}
return json_encode(array('values' => $values, 'einheit' => $einheit));
}
function getDayValues($raum, $value, $datum, $db){
$day_values = array();
$query = $db->query("SELECT * FROM 'SENSOR_DATA' WHERE ROOM == :room AND SENSORART == :value AND DATETIME >= :start AND DATETIME < :ende ORDER BY DATETIME ASC");
$query->execute(array('room' => $raum, 'value' => $value, 'start' => $datum." 00:00", 'ende'=> $datum." 23:59"));
foreach($query->fetchAll(PDO::FETCH_ASSOC) as $row){
$day_values[] = floatval($row['VALUE']);
}
if(max($day_values) == false && min($day_values)==false)
return null;
return array('max' => max($day_values), 'min' => min($day_values), 'date' => $datum);
}
function getDayData($raum, $value, $datum, $db){
$values = array();
$query = $db->query("SELECT * FROM 'SENSOR_DATA' WHERE ROOM == :room AND SENSORART == :value AND DATETIME >= :start AND DATETIME < :ende ORDER BY DATETIME ASC");
$query->execute(array('room' => $raum, 'value' => $value, 'start' => $datum." 00:00", 'ende'=> $datum." 23:59"));
foreach($query->fetchAll(PDO::FETCH_ASSOC) as $row){
$value_item = array('value' => floatval($row['VALUE']), 'time' => str_replace($datum." ", "", $row['DATETIME']));
array_push($values, $value_item);
}
$query = $db->query("SELECT * FROM 'ZWAVE_SENSOREN' WHERE RAUM == :room AND SENSORART == :value");
$query->execute(array('room' => $raum, 'value' => $value));
if($result = $query->fetch(PDO::FETCH_ASSOC)){
$einheit = $result['EINHEIT'];
}
return json_encode(array('values' => $values, 'einheit' => $einheit));
}
?>
getSensorData.php
In der Datei "getSensorData.php" werden die Zeilen 23-25 und 52-54 hinzugefügt, um einmal nur die Sensordaten zu den Räumen auszugeben, zu denen der Nutzer die Berechtigung hat und ein weiteres mal, um beim Abfragen von bestimmten Werten nur dann einen Wert zu erhalten, wenn die nötige Berechtigung vorliegt. Nach dem Bearbeiten wird das Skript wieder gespeichert und hochgeladen.<?php
function getSensorData($room, $val, $show_einheit, $db){
$ipAddress = $_SERVER['SERVER_ADDR'];
if($val == null){ //Alle Werte werden abgefragt
//Räume laden
if($room== "all"){
$results = $db->prepare("SELECT * FROM 'ROOMS'");
$results->execute();
}
else{
$results = $db->prepare("SELECT * FROM 'ROOMS' WHERE LOCATION == :room");
$results->execute(array('room' => $room));
}
//ausgabearray erzeugen
$values = array();
//Alle Räume durchlaufen
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($row['LOCATION'], $db)){
continue;
}
//Wertearray erzeugen
$value_array = array();
//Sensoren im aktuellen Raum laden
$ergebnisse = $db->prepare("SELECT * FROM 'ZWAVE_SENSOREN' WHERE RAUM == :location");
$ergebnisse->execute(array('location' => $row['LOCATION']));
//Alle Sensoren im Raum durchlaufen
foreach($ergebnisse->fetchAll(PDO::FETCH_ASSOC) as $reihe){
//Wert für jeden Sensor zusammen mit Sensorart in Wertearray schreiben
$value = array('shortform'=> $reihe['SHORTFORM'], 'sensorart' => $reihe['SENSORART'], 'wert' => getSensorData($reihe['RAUM'], $reihe['SENSORART'], 1, $db));
array_push($value_array, $value);
}
//Daten für aktuellen Raum in Ausgabearray schreiben
$value_item = array('name' => $row['NAME'], 'location' => $row['LOCATION'], 'value_array' => $value_array);
array_push($values, $value_item);
}
//JSON-Objekt zurückgeben
return json_encode(array('values' => $values));
}
else{ //Ein spezieller Wert wird abgefragt
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
//ID des gesuchten Sensors im gesuchten Raum ermitteln
$id = getZwaveId($room, $val, $db);
if($id !== null){
//Z-Wave API aufrufen
$link = "http://".$ipAddress."/ZAutomation/api/v1/devices/".$id;
//curl mit URL initialisieren
$curl = curl_init($link);
//Port setzen
curl_setopt($curl, CURLOPT_PORT, 8083);
//Ausgabe einstellen
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
//Abfrage ausführen
$result = curl_exec($curl);
//Wert je nach Wunsch mit/ohne Einheit ausgeben
$array = json_decode($result, true);
$return = $array['data']['metrics']['level'];
if($show_einheit == "1"){
$return = $return." ".$array['data']['metrics']['scaleTitle'];
}
return $return;
}
else return "N/A"; //Sensor-ID nicht gefunden
}
}
function getZwaveId($room, $val, $db){
//Z-Wave ID des gesuchten Sensors im geuchten Raum laden
$query = $db->prepare("SELECT * FROM 'ZWAVE_SENSOREN' WHERE RAUM == :room AND SENSORART == :sensorart");
$query->execute(array('room' => $room, 'sensorart' => $val));
//ID zurückgeben, wenn gefunden
if($result = $query->fetch(PDO::FETCH_ASSOC)){
return $result['ID'];
}
else return null; //ID nicht gefunden?
}
?>
createSene.php
In der Datei "createScene.php" wird der Code zweimal eingefügt, nämlich einmal in den Zeilen 6-8 und ein weiteres mal in den Zeilen 24-26. Damit wird sichergestellt, dass der aktuelle Nutzer nur Szenen in Räumen anlegen kann, zu denen er die Berechtigung hat (oder er weist die Szene keinem Raum zu) und dass er nur Aktionen in die Szene einbauen kann, zu deren Raum er die Berechtigung hat. Anschließend wird das Skript gespeichert und hochgeladen.<?php
function createScene($devices, $rooms, $types, $values, $conditions, $room, $name, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db)){
return "nopermission";
}
//Prüfen, ob alle Arrays ($devices, $rooms, $types, $modes) gleich viele Elemente haben, gibt andernfalls Fehlermeldung aus
if(sizeOf($devices) < sizeOf($rooms) ||
sizeOf($rooms) < sizeOf($types) ||
sizeOf($types) < sizeOf($values) ||
sizeOf($values) < sizeOf($devices)){
exit("error");
}
$scene = array();
//Die Übergabe-Arrays in ein einziges Array zusammensetzen
for($counter = 0; $counter < sizeOf($devices); $counter++){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($rooms[$counter], $db)){
return "nopermission";
}
$action_item = array('device' => $devices[$counter], 'location' => $rooms[$counter], 'value' => $values[$counter], 'type' => $types[$counter], 'if' => $conditions[$counter]);
array_push($scene, $action_item);
}
//Array als JSON-Objekt ausgeben
header('Content-type: application/json');
$action_string = json_encode(array('actions' => $scene));
//Prüfen, ob $room leer ist, wenn ja Default-Wert setzen
if($room == ''){
$room = 'NONE';
}
//Szene in Datenbank schreiben
$statement = $db->prepare("INSERT INTO SCENES (NAME, ROOM, ACTIONS) VALUES (?,?,?)");
$statement->execute(array($name, $room, $action_string));
return "ok";
}
?>
getScenes.php
In der Datei "getScenes.php" sorgen die Zeilen 16-18 dafür, dass jeder Nutzer nur die Szenen aus den Räumen ausgegeben bekommt, zu denen er Zugriff hat oder die keinem Raum zugewiesen wurden. Anschließend wird das Skript wieder gespeichert und auf den Server hochgeladen.<?php
function getScenes($room, $db){
//Szenen aus Datenbank laden
//INFO: Es werden alle Szenen abgefragt, die entweder dem Raum des Parameters $room oder dem Raum 'NONE' zugewiesen sind
$results = $db->prepare("SELECT * FROM 'SCENES' WHERE ROOM == :room OR ROOM == 'NONE'");
$results->execute(array('room' => $room));
$scenes = array();
//Namen aller gefundenen Szenen in Array schreiben
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($row['ROOM'], $db) && $row['ROOM'] !== "NONE"){
continue;
}
$scene_item = array('name' => $row['NAME'], 'room' => $row['ROOM'], 'actions' => $row['ACTIONS']);
array_push($scenes, $scene_item);
}
//Array als JSON-Objekt ausgeben
header('Content-type: application/json');
return json_encode(array('scenes' => $scenes));
}
?>
runScene.php
In der Datei "runScene.php" werden die Zeilen 6-8 hinzugefügt, um sicherzustellen, dass jeder Nutzer nur die Szenen der Räume ausführen kann, zu denen er die nötige Berechtigung hat. Danach wird das Skript wieder gespeichert und auf den Server hochgeladen.<?php
function runScene($room, $scene, $db){
//Diese Zeilen wurden hinzugefügt
if(!hasPermission($room, $db) && $room !== "NONE"){
return "nopermission";
}
//Szenen aus Datenbank laden
$results = $db->prepare("SELECT * FROM 'SCENES' WHERE ROOM == :room AND NAME == :scene");
$results->execute(array('room' => $room, 'scene' => $scene));
foreach($results->fetchAll(PDO::FETCH_ASSOC) as $row){
$array = json_decode($row['ACTIONS'], true);
foreach($array['actions'] as $action){
//Wenn Bedingung erfüllt ist oder keine Bedingung angegeben, führe Befehl aus
if($action['if']==null || conditionTrue($action['if'])){
//Verarbeitung der Aktion anhand des Aktionstyps (Schalter, Heizung, etc.)
switch($action['type']){
case 'switch':
setModes($action['location'], $action['device'], $action['value'], $db);
break;
case 'heizung':
//Heizungssteuerung wird noch implementiert
break;
}
}
}
}
return "ok";
}
//Prüft die übergebene Bedingung auf Wahrheit und gibt dementsprechend true/false zurück
function conditionTrue($condition){
switch($condition['type']){
//Sensorwert abfragen
case 'sensor':
$value = getData($condition['room'], $condition['sensorart'], '', $db);
break;
//Schalter abfragen
case 'switch':
$value = getModes($condition['room'], $condition['device'], $db);
break;
}
//vergleicht den abgefragten Wert mit dem übergebenen Wert
switch($condition['comparator']){
case '<':
return ($value < $condition['value']);
case '<=':
case '=<':
return ($value <= $condition['value']);
case '>':
return ($value > $condition['value']);
case '>=':
case '=>':
return ($value >= $condition['value']);
case '==':
case '=':
return ($value == $condition['value']);
default:
return false;
}
}
?>
Dieser Beitrag hat dir gefallen?
Dann abonniere doch unseren Newsletter!