#Settings New Architecture + Server Auth

dev-linux
Ivan Maslov 5 years ago
parent e438b6e0c6
commit 35f761736f

@ -21,7 +21,7 @@ logging.basicConfig(filename="Reports\ReportRun_"+datetime.datetime.now().strfti
#Единый глобальный словарь (За основу взять из Settings.py)
global mGlobalDict
mGlobalDict = Settings.mDict
mGlobalDict = Settings.Settings()
Processor.mGlobalDict = mGlobalDict
Timer.mGlobalDict = mGlobalDict
Timer.Processor.mGlobalDict = mGlobalDict
@ -38,12 +38,12 @@ lThreadServer = Server.RobotDaemonServer("ServerThread", mGlobalDict)
lThreadServer.start()
#Вечный цикл
while True:
lCurrentDateTime=datetime.datetime.now()
lCurrentDateTime = datetime.datetime.now()
#Циклический обход правил
lFlagSearchActivityType=True
for lIndex, lItem in enumerate(mGlobalDict["Scheduler"]["ActivityTimeList"]):
#Проверка дней недели, в рамках которых можно запускать активность
lItemWeekdayList=lItem.get("WeekdayList",[0,1,2,3,4,5,6])
lItemWeekdayList=lItem.get("WeekdayList", [0, 1, 2, 3, 4, 5, 6])
if lCurrentDateTime.weekday() in lItemWeekdayList:
if lFlagSearchActivityType:
#Лог

@ -4,11 +4,16 @@ from threading import Thread
import Processor
import importlib
import pdb
import base64
import uuid
import datetime
from http import cookies
from desktopmagic.screengrab_win32 import (
getDisplayRects, saveScreenToBmp, saveRectToBmp, getScreenAsImage,
getRectAsImage, getDisplaysAsImages)
global mGlobalDict
def SaveScreenshot(inFilePath):
# grab fullscreen
# Save the entire virtual screen as a PNG
@ -35,15 +40,87 @@ class RobotDaemonServer(Thread):
print('running server...')
httpd.serve_forever()
#Authenticate function ()
# return dict
# {
# "Domain": "", #Empty if Auth is not success
# "User": "" #Empty if Auth is not success
# }
def AuthenticateVerify(inRequest):
lResult={"Domain": "", "User": ""}
######################################
#Way 1 - try to find AuthToken
lCookies = cookies.SimpleCookie(inRequest.headers.get("Cookie", ""))
#pdb.set_trace()
if "AuthToken" in lCookies:
lCookieAuthToken = lCookies.get("AuthToken", "").value
if lCookieAuthToken:
#Find AuthToken in GlobalDict
if lCookieAuthToken in mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
#Auth Token Has Been Founded
lResult["Domain"] = mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"]
#Exit earlier
return lResult
######################################
#Way 2 - try to logon
lHeaderAuthorization = inRequest.headers.get("Authorization", "").split(" ")
if len(lHeaderAuthorization) == 2:
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(
":")
lUser = llHeaderAuthorizationDecodedUserPasswordList[0]
lPassword = llHeaderAuthorizationDecodedUserPasswordList[1]
lDomain = None
if "\\" in lUser:
lDomain = lUser.split("\\")[0]
lUser = lUser.split("\\")[1]
#Try to logon - use processor
lLogonResult = Processor.Activity(
{
"Type": "WindowsLogon",
"Domain": lDomain,
"User": lUser,
"Password": lPassword
}
)
#Check result
if lLogonResult["Result"]:
lResult["Domain"] = lLogonResult["Domain"]
lResult["User"] = lLogonResult["User"]
#Create token
lAuthToken=str(uuid.uuid1())
mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
mGlobalDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
#Set-cookie
inRequest.OpenRPA={}
inRequest.OpenRPA["AuthToken"] = lAuthToken
#inRequest.OpenRPAResponse["Set-Cookie"]=[]lResult["Set-Cookie"] = lAuthToken
#pdb.set_trace()
#inRequest.send_header("Set-Cookie:", f"AuthToken={lAuthToken}")
######################################
return lResult
def AuthenticateBlock(inRequest):
# Send response status code
inRequest.send_response(401)
# Send headers
inRequest.send_header('Content-type', 'text/html')
inRequest.send_header('WWW-Authenticate', 'Basic') # Always ask login pass
inRequest.end_headers()
# Write content as utf-8 data
inRequest.wfile.write(bytes("", "utf8"))
# HTTPRequestHandler class
class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
#ResponseContentTypeFile
def SendResponseContentTypeFile(self,inContentType,inFilePath):
def SendResponseContentTypeFile(self, inContentType, inFilePath):
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type',inContentType)
self.send_header('Content-type', inContentType)
#Check if var exist
if hasattr(self, "OpenRPA"):
self.send_header("Set-Cookie", f"AuthToken={self.OpenRPA['AuthToken']}")
self.end_headers()
lFileObject = open(inFilePath, "rb")
# Write content as utf-8 data
@ -52,125 +129,127 @@ class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
lFileObject.close()
# GET
def do_GET(self):
#Мост между файлом и http запросом (новый формат)
if self.path == "/":
self.SendResponseContentTypeFile('text/html',"Web\\Index.xhtml")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.css':
self.SendResponseContentTypeFile('text/css',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/jQuery/jquery-3.1.1.min.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Google/LatoItalic.css':
self.SendResponseContentTypeFile('font/css',"..\\Resources\\Web\\Google\\LatoItalic.css")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2':
self.SendResponseContentTypeFile('font/woff2',"..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2")
#Мост между файлом и http запросом (новый формат)
if self.path == '/favicon.ico':
self.SendResponseContentTypeFile('image/x-icon',"Web\\favicon.ico")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Handlebars/handlebars-v4.1.2.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js")
#Получить скриншот
if self.path.split("?")[0] == '/GetScreenshot':
#Сохранить файл на диск
SaveScreenshot("Screenshot.png")
self.SendResponseContentTypeFile('image/png',"Screenshot.png")
#Monitor
if self.path == '/Monitor/JSONDaemonListGet':
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
# Send message back to client
message = json.dumps(mGlobalDict)
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
if self.path == '/Monitor/ControlPanelDictGet':
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
#Create result JSON
lResultJSON={"RenderRobotList":[]}
lRenderFunctionsRobotList=mGlobalDict["ControlPanelDict"]["RobotList"]
for lItem in lRenderFunctionsRobotList:
#Подключить модуль для вызова
#print(globals())
lModuleImportName = lItem.get("RenderFunctionModuleSubfolderName","")
if lModuleImportName!="":
lModuleImportName = f'{lItem["RenderFunctionModuleSubfolderName"]}.{lItem["RenderFunctionModuleName"]}'
else:
lModuleImportName = lItem["RenderFunctionModuleName"]
lModule=importlib.import_module(lModuleImportName)
#Найти функцию
lFunction=getattr(lModule,lItem["RenderFunctionName"])
#Выполнить вызов и записать результат
lItemResultDict=lFunction(mGlobalDict)
#RunFunction
lResultJSON["RenderRobotList"].append(lItemResultDict)
# Send message back to client
message = json.dumps(lResultJSON)
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
#Filemanager function
if self.path.lower().startswith('/filemanager/'):
lFileURL=self.path[13:]
# check if file in FileURL - File Path Mapping Dict
if lFileURL.lower() in mGlobalDict["FileManager"]["FileURLFilePathDict"]:
self.SendResponseContentTypeFile('application/octet-stream',mGlobalDict["FileManager"]["FileURLFilePathDict"][lFileURL])
# Auth function
if self.path.lower().startswith('/auth'):
# Send response status code
self.send_response(401)
# Send headers
self.send_header('Content-type','text/html')
self.send_header('WWW-Authenticate', 'Basic') #Always ask login pass
lFlagIsKerberos=False
lHeaderAuthorization = self.headers.get("Authorization","").split(" ")
import base64
if len(lHeaderAuthorization) == 2:
llHeaderAuthorizationDecodedUserPasswordList = base64.b64decode(lHeaderAuthorization[1]).decode("utf-8").split(":")
lUser = llHeaderAuthorizationDecodedUserPasswordList[0]
lPassword = llHeaderAuthorizationDecodedUserPasswordList[1]
lDomain = None
if "\\" in lUser:
lDomain = lUser.split("\\")[0]
lUser = lUser.split("\\")[1]
#print(f"Header Authorization, domain: {lDomain}, login: {lUser}, password: {lPassword}")
#self.send_header('Vary', 'negotiative')
#self.send_header('TCN', 'choice')
self.end_headers()
# Write content as utf-8 data
self.wfile.write(bytes("", "utf8"))
#pdb.set_trace()
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
if mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lAuthenticateDict = AuthenticateVerify(self)
if not lAuthenticateDict["User"]:
lFlagAccessUserBlock=True
if lFlagAccessUserBlock:
AuthenticateBlock(self)
#####################################
else:
#Мост между файлом и http запросом (новый формат)
if self.path == "/":
self.SendResponseContentTypeFile('text/html', "Web\\Index.xhtml")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.css':
self.SendResponseContentTypeFile('text/css',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.css")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/semantic.min.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Semantic-UI-CSS-master\\semantic.min.js")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/jQuery/jquery-3.1.1.min.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\jQuery\\jquery-3.1.1.min.js")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Google/LatoItalic.css':
self.SendResponseContentTypeFile('font/css',"..\\Resources\\Web\\Google\\LatoItalic.css")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Semantic-UI-CSS-master/themes/default/assets/fonts/icons.woff2':
self.SendResponseContentTypeFile('font/woff2',"..\\Resources\\Web\\Semantic-UI-CSS-master\\themes\\default\\assets\\fonts\\icons.woff2")
#Мост между файлом и http запросом (новый формат)
if self.path == '/favicon.ico':
self.SendResponseContentTypeFile('image/x-icon',"Web\\favicon.ico")
#Мост между файлом и http запросом (новый формат)
if self.path == '/3rdParty/Handlebars/handlebars-v4.1.2.js':
self.SendResponseContentTypeFile('application/javascript',"..\\Resources\\Web\\Handlebars\\handlebars-v4.1.2.js")
#Получить скриншот
if self.path.split("?")[0] == '/GetScreenshot':
#Сохранить файл на диск
SaveScreenshot("Screenshot.png")
self.SendResponseContentTypeFile('image/png',"Screenshot.png")
#Monitor
if self.path == '/Monitor/JSONDaemonListGet':
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
# Send message back to client
message = json.dumps(mGlobalDict)
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
if self.path == '/Monitor/ControlPanelDictGet':
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
#Create result JSON
lResultJSON={"RenderRobotList":[]}
lRenderFunctionsRobotList=mGlobalDict["ControlPanelDict"]["RobotList"]
for lItem in lRenderFunctionsRobotList:
#Подключить модуль для вызова
#print(globals())
lModuleImportName = lItem.get("RenderFunctionModuleSubfolderName","")
if lModuleImportName!="":
lModuleImportName = f'{lItem["RenderFunctionModuleSubfolderName"]}.{lItem["RenderFunctionModuleName"]}'
else:
lModuleImportName = lItem["RenderFunctionModuleName"]
lModule=importlib.import_module(lModuleImportName)
#Найти функцию
lFunction=getattr(lModule, lItem["RenderFunctionName"])
#Выполнить вызов и записать результат
lItemResultDict=lFunction(mGlobalDict)
#RunFunction
lResultJSON["RenderRobotList"].append(lItemResultDict)
# Send message back to client
message = json.dumps(lResultJSON)
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
#Filemanager function
if self.path.lower().startswith('/filemanager/'):
lFileURL=self.path[13:]
# check if file in FileURL - File Path Mapping Dict
if lFileURL.lower() in mGlobalDict["FileManager"]["FileURLFilePathDict"]:
self.SendResponseContentTypeFile('application/octet-stream',mGlobalDict["FileManager"]["FileURLFilePathDict"][lFileURL])
# Auth function
# POST
def do_POST(self):
#Централизованная функция получения запросов/отправки
if self.path == '/Utils/Processor':
#ReadRequest
lInputObject={}
if self.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(self.headers.get('Content-Length'))
lInputByteArray=self.rfile.read(lInputByteArrayLength)
#Превращение массива байт в объект
lInputObject=json.loads(lInputByteArray.decode('utf8'))
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
# Send message back to client
message = json.dumps(Processor.ActivityListOrDict(lInputObject))
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
return
#print(ChildProcessReadWaitString(p))
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
if mGlobalDict.get("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lAuthenticateDict = AuthenticateVerify(self)
if not lAuthenticateDict["User"]:
lFlagAccessUserBlock=True
if lFlagAccessUserBlock:
AuthenticateBlock(self)
#####################################
else:
#Централизованная функция получения запросов/отправки
if self.path == '/Utils/Processor':
#ReadRequest
lInputObject={}
if self.headers.get('Content-Length') is not None:
lInputByteArrayLength = int(self.headers.get('Content-Length'))
lInputByteArray=self.rfile.read(lInputByteArrayLength)
#Превращение массива байт в объект
lInputObject=json.loads(lInputByteArray.decode('utf8'))
# Send response status code
self.send_response(200)
# Send headers
self.send_header('Content-type','application/json')
self.end_headers()
# Send message back to client
message = json.dumps(Processor.ActivityListOrDict(lInputObject))
# Write content as utf-8 data
self.wfile.write(bytes(message, "utf8"))
return

@ -64,77 +64,111 @@ def CheckIfProcessRunning(processName):
return False;
#Orchestrator settings
mDict = {
"Server":{
"ListenPort_":"Порт, по которому можно подключиться к демону",
"ListenPort":8081,
"ListenURLList":[
{
"Description":"Local machine test",
"URL_":"Сетевое расположение сервера демона",
"URL":"http://127.0.0.1:8081"
}
]
},
"Scheduler":{
"ActivityTimeCheckLoopSeconds":5, #Количество секунд, между циклами проверки действий
"ActivityTimeList":[
{
"TimeHH:MM":"19:25", #Time [HH:MM] to trigger activity
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type":"ProcessStart", #Activity type
"Path":"Notepad", #Executable file path
"ArgList": [] #List of the arguments
def Settings():
mDict = {
"Server": {
"ListenPort_": "Порт, по которому можно подключиться к демону",
"ListenPort": 8081,
"ListenURLList": [
{
"Description": "Local machine test",
"URL_": "Сетевое расположение сервера демона",
"URL": "http://127.0.0.1:8081"
}
],
"AccessUsers": {
"FlagCredentialsAsk": True, #Turn on Authentication
"FlagNoRulesBlock": True, #Block request, if user has no rules for requested URL (if FlagCredentialAsk is turned on)
"UserDict": {
#("Domain", "User"): {
# "FunctionDict": {
# # ("Method|GET|POST", "Match type|BeginWith|Contains", "URL"): def function (inRequest, inGlobalDict, inAuthenticateDict)
# }
#}
},
"AuthTokensDict": {
#"<AuthToken>":{"User":"", "Domain":"", "TokenDatetime":<Datetime>}
}
},
{
"TimeHH:MM":"19:20", #Time [HH:MM] to trigger activity
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type":"ProcessStop", #Activity type
"Name":"OpenRPARobotDaemon.exe", #Process name
"FlagForce":True, #Force process close
"User": "%username%" #Empty, user or %username%
}
},
"Scheduler": {
"ActivityTimeCheckLoopSeconds":5, #Количество секунд, между циклами проверки действий
"ActivityTimeList":[
{
"TimeHH:MM":"19:25", #Time [HH:MM] to trigger activity
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type":"ProcessStart", #Activity type
"Path":"Notepad", #Executable file path
"ArgList": [] #List of the arguments
}
},
{
"TimeHH:MM":"19:20", #Time [HH:MM] to trigger activity
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type":"ProcessStop", #Activity type
"Name":"OpenRPARobotDaemon.exe", #Process name
"FlagForce":True, #Force process close
"User": "%username%" #Empty, user or %username%
}
},
{
"TimeHH:MMStart":"19:20", #Time [HH:MM] to trigger activity
"TimeHH:MMStop":"19:20",
"ActivityIntervalSeconds":5,
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type": "PythonStart", #Activity type
"ModuleName": "CheckActivity", #Python function module name
"FunctionName": "test_activity", #Python function name
"ArgList": ["TestArg1","TestArg2"] #Input python function args
}
}
},
{
"TimeHH:MMStart":"19:20", #Time [HH:MM] to trigger activity
"TimeHH:MMStop":"19:20",
"ActivityIntervalSeconds":5,
"WeekdayList":[1,2,3], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7]
"Activity":{
"Type": "PythonStart", #Activity type
"ModuleName": "CheckActivity", #Python function module name
"FunctionName": "test_activity", #Python function name
"ArgList": ["TestArg1","TestArg2"] #Input python function args
],
"LogList":[]
},
"Processor":{
"LogType_CMDStart":True, #LogType_<Type>: <bool> if Trace for command is selected for False, the tracing will be off for such activity type. Default True
"LogList":[] #List of processor activity, which was executed. Fill list when orchestrator is running
},
"ControlPanelDict":{
"RefreshSeconds": 5,
"RobotList": [
{
"RenderFunctionName":"RenderRobotR01",
"RenderFunctionModuleName":"Settings",
"RenderFunctionModuleSubfolderName":"Settings"
}
]
},
"FileManager": {
"FileURLFilePathDict_help":"https://localhost:8081/filemanager/<file URL>. All FileURL s must be set in lowercase",
"FileURLFilePathDict":{
"r01/report.xlsx":"C:\\RPA\\R01_IntegrationOrderOut\\Data\\Reestr_otgruzok.xlsx"
}
],
"LogList":[]
},
"Processor":{
"LogType_CMDStart":True, #LogType_<Type>: <bool> if Trace for command is selected for False, the tracing will be off for such activity type. Default True
"LogList":[] #List of processor activity, which was executed. Fill list when orchestrator is running
},
"ControlPanelDict":{
"RefreshSeconds": 5,
"RobotList": [
{
"RenderFunctionName":"RenderRobotR01",
"RenderFunctionModuleName":"Settings",
"RenderFunctionModuleSubfolderName":"Settings"
}
]
},
"FileManager": {
"FileURLFilePathDict_help":"https://localhost:8081/filemanager/<file URL>. All FileURL s must be set in lowercase",
"FileURLFilePathDict":{
"r01/report.xlsx":"C:\\RPA\\R01_IntegrationOrderOut\\Data\\Reestr_otgruzok.xlsx"
}
},
"Storage":{
"Robot_R01_help":"Robot data storage in orchestrator env",
"Robot_R01":{}
}
}
},
"Storage":{
"Robot_R01_help":"Robot data storage in orchestrator env",
"Robot_R01":{}
}
}
###################################
#Init .py files from Settings folder
####################################
#Get file list from Settings folder
import os
import pdb
lSettingsPath = os.path.join(os.getcwd(), "Settings")
#pdb.set_trace()
#Lambda function to get files .py from settings folder except Settings.py
lFileList = [f for f in os.listdir(lSettingsPath) if os.path.isfile(os.path.join(lSettingsPath, f)) and f.split(".")[-1]=="py" and f != "Settings.py"]
#TODO Import files as python module and call SettingsUpdate()
import importlib.util
for lModuleFilePathItem in lFileList
spec = importlib.util.spec_from_file_location("module.name", "/path/to/file.py")
foo = importlib.util.module_from_spec(spec)
#spec.loader.exec_module(foo)
#foo.MyClass()
#Return Global settings
return mDict

@ -0,0 +1,2 @@
def SettingsUpdate():
print("Everything is ok!")
Loading…
Cancel
Save