mirror of
https://github.com/naruxde/revpipyload.git
synced 2025-11-08 23:23:52 +01:00
IpAclManager ausgelagert in shared
ACLs über Datei laden (Eine ACL pro Zeile) ProcimgServer Parameter aclmode entfernt Codestyle
This commit is contained in:
@@ -12,110 +12,6 @@ from re import match as rematch
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
|
||||
class IpAclManager():
|
||||
|
||||
"""Verwaltung fuer IP Adressen und deren ACL Level."""
|
||||
|
||||
def __init__(self, minlevel, maxlevel, acl=None):
|
||||
"""Init IpAclManager class.
|
||||
|
||||
@param minlevel Smallest access level (min. 0)
|
||||
@param maxlevel Biggest access level (max. 9)
|
||||
@param acl ACL Liste fuer Berechtigungen als <class 'str'>
|
||||
|
||||
"""
|
||||
if type(minlevel) != int:
|
||||
raise ValueError("parameter minlevel must be <class 'int'>")
|
||||
if type(maxlevel) != int:
|
||||
raise ValueError("parameter maxlevel must be <class 'int'>")
|
||||
if minlevel < 0:
|
||||
raise ValueError("minlevel must be 0 or more")
|
||||
if maxlevel > 9:
|
||||
raise ValueError("maxlevel maximum is 9")
|
||||
if minlevel > maxlevel:
|
||||
raise ValueError("minlevel is smaller than maxlevel")
|
||||
|
||||
self.__dict_acl = {}
|
||||
self.__dict_regex = {}
|
||||
self.__dict_knownips = {}
|
||||
self.__re_ipacl = "(([\\d\\*]{1,3}\\.){3}[\\d\\*]{1,3},[" \
|
||||
+ str(minlevel) + "-" + str(maxlevel) + "] ?)*"
|
||||
|
||||
# Liste erstellen, wenn übergeben
|
||||
if acl is not None:
|
||||
self.__set_acl(acl)
|
||||
|
||||
def __iter__(self):
|
||||
"""Gibt einzelne ACLs als <class 'tuple'> aus."""
|
||||
for aclip in sorted(self.__dict_acl):
|
||||
yield (aclip, self.__dict_acl[aclip])
|
||||
|
||||
def __get_acl(self):
|
||||
"""Getter fuer den rohen ACL-String.
|
||||
return ACLs als <class 'str'>"""
|
||||
str_acl = ""
|
||||
for aclip in sorted(self.__dict_acl):
|
||||
str_acl += "{},{} ".format(aclip, self.__dict_acl[aclip])
|
||||
return str_acl.strip()
|
||||
|
||||
def __get_regex_acl(self):
|
||||
"""Gibt formatierten RegEx-String zurueck.
|
||||
return RegEx Code als <class 'str'>"""
|
||||
return self.__re_ipacl
|
||||
|
||||
def __set_acl(self, value):
|
||||
"""Uebernimmt neue ACL-Liste fuer die Ausertung der Level.
|
||||
@param value Neue ACL-Liste als <class 'str'>"""
|
||||
if type(value) != str:
|
||||
raise ValueError("parameter acl must be <class 'str'>")
|
||||
|
||||
value = value.strip()
|
||||
if not refullmatch(self.__re_ipacl, value):
|
||||
raise ValueError("acl format ist not okay - 1.2.3.4,0 5.6.7.8,1")
|
||||
|
||||
# Klassenwerte übernehmen
|
||||
self.__dict_acl = {}
|
||||
self.__dict_regex = {}
|
||||
self.__dict_knownips = {}
|
||||
|
||||
# Liste neu füllen mit regex Strings
|
||||
for ip_level in value.split():
|
||||
ip, level = ip_level.split(",", 1)
|
||||
self.__dict_acl[ip] = int(level)
|
||||
self.__dict_regex[ip] = \
|
||||
ip.replace(".", "\\.").replace("*", "\\d{1,3}")
|
||||
|
||||
def get_acllevel(self, ipaddress):
|
||||
"""Prueft IP gegen ACL List und gibt ACL-Wert aus.
|
||||
@param ipaddress zum pruefen
|
||||
@return <class 'int'> ACL Wert oder -1 wenn nicht gefunden"""
|
||||
# Bei bereits aufgelösten IPs direkt ACL auswerten
|
||||
if ipaddress in self.__dict_knownips:
|
||||
return self.__dict_knownips[ipaddress]
|
||||
|
||||
for aclip in sorted(self.__dict_acl, reverse=True):
|
||||
if refullmatch(self.__dict_regex[aclip], ipaddress):
|
||||
# IP und Level merken
|
||||
self.__dict_knownips[ipaddress] = self.__dict_acl[aclip]
|
||||
|
||||
# Level zurückgeben
|
||||
return self.__dict_acl[aclip]
|
||||
|
||||
return -1
|
||||
|
||||
def loadacl(self, str_acl):
|
||||
"""Laed ACL String und gibt erfolg zurueck.
|
||||
@param str_acl ACL als <class 'str'>
|
||||
@return True, wenn erfolgreich uebernommen"""
|
||||
if not refullmatch(self.__re_ipacl, str_acl):
|
||||
return False
|
||||
self.__set_acl(str_acl)
|
||||
return True
|
||||
|
||||
acl = property(__get_acl, __set_acl)
|
||||
regex_acl = property(__get_regex_acl)
|
||||
|
||||
|
||||
def _setuprt(pid, evt_exit):
|
||||
"""Konfiguriert Programm fuer den RT-Scheduler.
|
||||
@param pid PID, der angehoben werden soll
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"""Modul fuer die Verwaltung der PLC-Slave Funktionen."""
|
||||
import proginit
|
||||
import socket
|
||||
from shared.ipaclmanager import IpAclManager
|
||||
from threading import Event, Thread
|
||||
from timeit import default_timer
|
||||
|
||||
@@ -26,8 +27,13 @@ class RevPiSlave(Thread):
|
||||
|
||||
def __init__(self, ipacl, port=55234):
|
||||
"""Instantiiert RevPiSlave-Klasse.
|
||||
@param acl Stringliste mit Leerstellen getrennt
|
||||
@param ipacl AclManager <class 'IpAclManager'>
|
||||
@param port Listen Port fuer plc Slaveserver"""
|
||||
if not type(ipacl) == IpAclManager:
|
||||
raise ValueError("parameter ipacl must be <class 'IpAclManager'>")
|
||||
if not type(port) == int:
|
||||
raise ValueError("parameter port must be <class 'int'>")
|
||||
|
||||
super().__init__()
|
||||
self.__ipacl = ipacl
|
||||
self._evt_exit = Event()
|
||||
|
||||
@@ -28,17 +28,12 @@ class ProcimgServer():
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, xmlserver, aclmode):
|
||||
def __init__(self, xmlserver):
|
||||
"""Instantiiert RevPiCheckServer()-Klasse.
|
||||
|
||||
@param xmlserver XML-RPC Server
|
||||
@param aclmode Zugriffsrechte
|
||||
|
||||
"""
|
||||
@param xmlserver XML-RPC Server"""
|
||||
# Logger übernehmen
|
||||
proginit.logger.debug("enter ProcimgServer.__init__()")
|
||||
|
||||
self.acl = aclmode
|
||||
self.rpi = None
|
||||
|
||||
# XML-Server übernehmen
|
||||
@@ -121,13 +116,6 @@ class ProcimgServer():
|
||||
@return list() [device, io, status, msg]
|
||||
|
||||
"""
|
||||
# Zugriffsrechte prüfen
|
||||
if self.acl < 3:
|
||||
return [
|
||||
device, io, False,
|
||||
"not allowed in XML-RPC permission mode {}".format(self.acl)
|
||||
]
|
||||
|
||||
# Binary() in bytes() umwandeln
|
||||
if type(value) == Binary:
|
||||
value = value.data
|
||||
@@ -192,9 +180,8 @@ class ProcimgServer():
|
||||
for xmlfunc in self.xmlreadfuncs:
|
||||
if xmlfunc in self.xmlsrv.funcs:
|
||||
del self.xmlsrv.funcs[xmlfunc]
|
||||
if self.acl >= 3:
|
||||
for xmlfunc in self.xmlwritefuncs:
|
||||
if xmlfunc in self.xmlsrv.funcs:
|
||||
del self.xmlsrv.funcs[xmlfunc]
|
||||
for xmlfunc in self.xmlwritefuncs:
|
||||
if xmlfunc in self.xmlsrv.funcs:
|
||||
del self.xmlsrv.funcs[xmlfunc]
|
||||
|
||||
proginit.logger.debug("leave ProcimgServer.stop()")
|
||||
|
||||
@@ -40,8 +40,9 @@ import signal
|
||||
import tarfile
|
||||
import zipfile
|
||||
from configparser import ConfigParser
|
||||
from helper import refullmatch, IpAclManager
|
||||
from helper import refullmatch
|
||||
from json import loads as jloads
|
||||
from shared.ipaclmanager import IpAclManager
|
||||
from shutil import rmtree
|
||||
from tempfile import mkstemp
|
||||
from threading import Event
|
||||
@@ -49,7 +50,7 @@ from time import asctime
|
||||
from xmlrpc.client import Binary
|
||||
from xrpcserver import SaveXMLRPCServer
|
||||
|
||||
pyloadversion = "0.6.1"
|
||||
pyloadversion = "0.6.2"
|
||||
|
||||
|
||||
class RevPiPyLoad():
|
||||
@@ -140,8 +141,8 @@ class RevPiPyLoad():
|
||||
self.plcslave = \
|
||||
int(self.globalconfig["PLCSLAVE"].get("plcslave", 0))
|
||||
self.plcslaveacl = IpAclManager(minlevel=0, maxlevel=1)
|
||||
if not self.plcslaveacl.loadacl(
|
||||
self.globalconfig["PLCSLAVE"].get("acl", "")):
|
||||
if not self.plcslaveacl.loadaclfile(
|
||||
self.globalconfig["PLCSLAVE"].get("aclfile", "")):
|
||||
proginit.logger.warning(
|
||||
"can not load plcslave acl - wrong format"
|
||||
)
|
||||
@@ -163,8 +164,8 @@ class RevPiPyLoad():
|
||||
self.xmlrpc = \
|
||||
int(self.globalconfig["XMLRPC"].get("xmlrpc", 0))
|
||||
self.xmlrpcacl = IpAclManager(minlevel=0, maxlevel=4)
|
||||
if not self.xmlrpcacl.loadacl(
|
||||
self.globalconfig["XMLRPC"].get("acl", "")):
|
||||
if not self.xmlrpcacl.loadaclfile(
|
||||
self.globalconfig["XMLRPC"].get("aclfile", "")):
|
||||
proginit.logger.warning(
|
||||
"can not load xmlrpc acl - wrong format"
|
||||
)
|
||||
@@ -228,9 +229,7 @@ class RevPiPyLoad():
|
||||
# Erweiterte Funktionen anmelden
|
||||
try:
|
||||
import procimgserver
|
||||
self.xml_ps = procimgserver.ProcimgServer(
|
||||
self.xsrv, self.xmlrpc
|
||||
)
|
||||
self.xml_ps = procimgserver.ProcimgServer(self.xsrv)
|
||||
self.xsrv.register_function(1, self.xml_psstart, "psstart")
|
||||
self.xsrv.register_function(1, self.xml_psstop, "psstop")
|
||||
except:
|
||||
@@ -719,11 +718,46 @@ class RevPiPyLoad():
|
||||
)
|
||||
)
|
||||
return False
|
||||
self.globalconfig.set(
|
||||
sektion,
|
||||
key if localkey == "" else localkey,
|
||||
str(dc[key])
|
||||
if localkey != "acl":
|
||||
self.globalconfig.set(
|
||||
sektion,
|
||||
key if localkey == "" else localkey,
|
||||
str(dc[key])
|
||||
)
|
||||
|
||||
# ACLs sofort übernehmen und schreiben
|
||||
str_acl = dc.get("plcslaveacl", None)
|
||||
if str_acl is not None and self.plcslaveacl.acl != str_acl:
|
||||
self.plcslaveacl.acl = str_acl
|
||||
if not self.plcslaveacl.writeaclfile(aclname="PLC-SLAVE"):
|
||||
proginit.logger.error(
|
||||
"can not write acl file '{}' for PLC-SLAVE".format(
|
||||
self.plcslaveacl.filename
|
||||
)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
proginit.logger.info(
|
||||
"wrote new acl file '{}' for PLC-SLAVE".format(
|
||||
self.plcslaveacl.filename
|
||||
)
|
||||
)
|
||||
str_acl = dc.get("xmlrpcacl", None)
|
||||
if str_acl is not None and self.xmlrpcacl.acl != str_acl:
|
||||
self.xmlrpcacl.acl = str_acl
|
||||
if not self.xmlrpcacl.writeaclfile(aclname="XML-RPC"):
|
||||
proginit.logger.error(
|
||||
"can not write acl file '{}' for XML-RPC".format(
|
||||
self.xmlrpcacl.filename
|
||||
)
|
||||
)
|
||||
return False
|
||||
else:
|
||||
proginit.logger.info(
|
||||
"wrote new acl file '{}' for XML-RPC".format(
|
||||
self.xmlrpcacl.filename
|
||||
)
|
||||
)
|
||||
|
||||
# conf-Datei schreiben
|
||||
with open(proginit.globalconffile, "w") as fh:
|
||||
|
||||
1
revpipyload/shared/__init__.py
Normal file
1
revpipyload/shared/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
"""Shared modules."""
|
||||
189
revpipyload/shared/ipaclmanager.py
Normal file
189
revpipyload/shared/ipaclmanager.py
Normal file
@@ -0,0 +1,189 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# IpAclManager
|
||||
#
|
||||
# (c) Sven Sager, License: LGPLv3
|
||||
# Version 0.1.0
|
||||
#
|
||||
"""Verwaltet IP Adressen und deren ACLs."""
|
||||
from os import access, R_OK, W_OK
|
||||
from re import match as rematch
|
||||
|
||||
|
||||
def refullmatch(regex, string):
|
||||
"""re.fullmatch wegen alter python version aus wheezy nachgebaut.
|
||||
|
||||
@param regex RegEx Statement
|
||||
@param string Zeichenfolge gegen die getestet wird
|
||||
@return True, wenn komplett passt sonst False
|
||||
|
||||
"""
|
||||
m = rematch(regex, string)
|
||||
return m is not None and m.end() == len(string)
|
||||
|
||||
|
||||
class IpAclManager():
|
||||
|
||||
"""Verwaltung fuer IP Adressen und deren ACL Level."""
|
||||
|
||||
def __init__(self, minlevel, maxlevel, acl=None):
|
||||
"""Init IpAclManager class.
|
||||
|
||||
@param minlevel Smallest access level (min. 0)
|
||||
@param maxlevel Biggest access level (max. 9)
|
||||
@param acl ACL Liste fuer Berechtigungen als <class 'str'>
|
||||
|
||||
"""
|
||||
if type(minlevel) != int:
|
||||
raise ValueError("parameter minlevel must be <class 'int'>")
|
||||
if type(maxlevel) != int:
|
||||
raise ValueError("parameter maxlevel must be <class 'int'>")
|
||||
if minlevel < 0:
|
||||
raise ValueError("minlevel must be 0 or more")
|
||||
if maxlevel > 9:
|
||||
raise ValueError("maxlevel maximum is 9")
|
||||
if minlevel > maxlevel:
|
||||
raise ValueError("minlevel is smaller than maxlevel")
|
||||
|
||||
self.__dict_acl = {}
|
||||
self.__dict_regex = {}
|
||||
self.__dict_knownips = {}
|
||||
self.__filename = None
|
||||
self.__re_ipacl = "(([\\d\\*]{1,3}\\.){3}[\\d\\*]{1,3},[" \
|
||||
+ str(minlevel) + "-" + str(maxlevel) + "] ?)*"
|
||||
|
||||
# Liste erstellen, wenn übergeben
|
||||
if acl is not None:
|
||||
self.__set_acl(acl)
|
||||
|
||||
def __iter__(self):
|
||||
"""Gibt einzelne ACLs als <class 'tuple'> aus."""
|
||||
for aclip in sorted(self.__dict_acl):
|
||||
yield (aclip, self.__dict_acl[aclip])
|
||||
|
||||
def __get_acl(self):
|
||||
"""Getter fuer den rohen ACL-String.
|
||||
return ACLs als <class 'str'>"""
|
||||
str_acl = ""
|
||||
for aclip in sorted(self.__dict_acl):
|
||||
str_acl += "{},{} ".format(aclip, self.__dict_acl[aclip])
|
||||
return str_acl.strip()
|
||||
|
||||
def __get_filename(self):
|
||||
"""Getter fuer Dateinamen.
|
||||
@return Filename der ACL <class 'str'>"""
|
||||
return "" if self.__filename is None else self.__filename
|
||||
|
||||
def __get_regex_acl(self):
|
||||
"""Gibt formatierten RegEx-String zurueck.
|
||||
return RegEx Code als <class 'str'>"""
|
||||
return self.__re_ipacl
|
||||
|
||||
def __set_acl(self, value):
|
||||
"""Uebernimmt neue ACL-Liste fuer die Ausertung der Level.
|
||||
@param value Neue ACL-Liste als <class 'str'>"""
|
||||
if type(value) != str:
|
||||
raise ValueError("parameter acl must be <class 'str'>")
|
||||
|
||||
value = value.strip()
|
||||
if not refullmatch(self.__re_ipacl, value):
|
||||
raise ValueError("acl format ist not okay - 1.2.3.4,0 5.6.7.8,1")
|
||||
|
||||
# Klassenwerte übernehmen
|
||||
self.__dict_acl = {}
|
||||
self.__dict_regex = {}
|
||||
self.__dict_knownips = {}
|
||||
|
||||
# Liste neu füllen mit regex Strings
|
||||
for ip_level in value.split():
|
||||
ip, level = ip_level.split(",", 1)
|
||||
self.__dict_acl[ip] = int(level)
|
||||
self.__dict_regex[ip] = \
|
||||
ip.replace(".", "\\.").replace("*", "\\d{1,3}")
|
||||
|
||||
def get_acllevel(self, ipaddress):
|
||||
"""Prueft IP gegen ACL List und gibt ACL-Wert aus.
|
||||
@param ipaddress zum pruefen
|
||||
@return <class 'int'> ACL Wert oder -1 wenn nicht gefunden"""
|
||||
# Bei bereits aufgelösten IPs direkt ACL auswerten
|
||||
if ipaddress in self.__dict_knownips:
|
||||
return self.__dict_knownips[ipaddress]
|
||||
|
||||
for aclip in sorted(self.__dict_acl, reverse=True):
|
||||
if refullmatch(self.__dict_regex[aclip], ipaddress):
|
||||
# IP und Level merken
|
||||
self.__dict_knownips[ipaddress] = self.__dict_acl[aclip]
|
||||
|
||||
# Level zurückgeben
|
||||
return self.__dict_acl[aclip]
|
||||
|
||||
return -1
|
||||
|
||||
def loadacl(self, str_acl):
|
||||
"""Laed ACL String und gibt erfolg zurueck.
|
||||
@param str_acl ACL als <class 'str'>
|
||||
@return True, wenn erfolgreich uebernommen"""
|
||||
if not refullmatch(self.__re_ipacl, str_acl):
|
||||
return False
|
||||
self.__set_acl(str_acl)
|
||||
return True
|
||||
|
||||
def loadaclfile(self, filename):
|
||||
"""Laed ACL Definitionen aus Datei.
|
||||
@param filename Dateiname fuer Definitionen
|
||||
@return True, wenn Laden erfolgreich war"""
|
||||
if type(filename) != str:
|
||||
raise ValueError("parameter filename must be <class 'str'>")
|
||||
|
||||
# Zugriffsrecht prüfen
|
||||
if not access(filename, R_OK):
|
||||
return False
|
||||
|
||||
str_acl = ""
|
||||
with open(filename, "r") as fh:
|
||||
while True:
|
||||
buff = fh.readline()
|
||||
if buff == "":
|
||||
break
|
||||
buff = buff.split("#")[0].strip()
|
||||
if len(buff) > 0:
|
||||
str_acl += buff + " "
|
||||
|
||||
acl_okay = self.loadacl(str_acl.strip())
|
||||
if acl_okay:
|
||||
# Dateinamen für Schreiben übernehmen
|
||||
self.__filename = filename
|
||||
|
||||
return acl_okay
|
||||
|
||||
def writeaclfile(self, filename=None, aclname=None):
|
||||
"""Schreibt ACL Definitionen in Datei.
|
||||
@param filename Dateiname fuer Definitionen
|
||||
@return True, wenn Schreiben erfolgreich war"""
|
||||
if filename is not None and type(filename) != str:
|
||||
raise ValueError("parameter filename must be <class 'str'>")
|
||||
if aclname is not None and type(aclname) != str:
|
||||
raise ValueError("parameter aclname must be <class 'str'>")
|
||||
|
||||
# Dateinamen prüfen
|
||||
if filename is None and self.__filename is not None:
|
||||
filename = self.__filename
|
||||
|
||||
# Zugriffsrecht prüfen
|
||||
if not access(filename, W_OK):
|
||||
return False
|
||||
|
||||
header = "# {}Access Control List (acl)\n" \
|
||||
"# One entry per Line IPADRESS,LEVEL\n" \
|
||||
"#\n".format("" if aclname is None else aclname + " ")
|
||||
|
||||
with open(filename, "w") as fh:
|
||||
fh.write(header)
|
||||
for aclip in sorted(self.__dict_acl):
|
||||
fh.write("{},{}\n".format(aclip, self.__dict_acl[aclip]))
|
||||
|
||||
return True
|
||||
|
||||
acl = property(__get_acl, __set_acl)
|
||||
filename = property(__get_filename)
|
||||
regex_acl = property(__get_regex_acl)
|
||||
@@ -7,7 +7,7 @@
|
||||
#
|
||||
"""XML-RPC Server anpassungen fuer Absicherung."""
|
||||
import proginit
|
||||
from helper import IpAclManager
|
||||
from shared.ipaclmanager import IpAclManager
|
||||
from concurrent import futures
|
||||
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||||
|
||||
@@ -19,9 +19,13 @@ class SaveXMLRPCServer(SimpleXMLRPCServer):
|
||||
def __init__(
|
||||
self, addr, logRequests=True, allow_none=False,
|
||||
use_builtin_types=False, ipacl=None):
|
||||
"""Init SaveXMLRPCServer class."""
|
||||
"""Init SaveXMLRPCServer class.
|
||||
@param ipacl AclManager <class 'IpAclManager'>"""
|
||||
proginit.logger.debug("enter SaveXMLRPCServer.__init__()")
|
||||
|
||||
if ipacl is not None and type(ipacl) != IpAclManager:
|
||||
raise ValueError("parameter ipacl must be <class 'IpAclManager'>")
|
||||
|
||||
# Vererbte Klasse instantiieren
|
||||
super().__init__(
|
||||
addr=addr,
|
||||
|
||||
Reference in New Issue
Block a user