diff --git a/Sources/pyOpenRPA/Orchestrator/Server.py b/Sources/pyOpenRPA/Orchestrator/Server.py index 0349fcee..1bd7c765 100755 --- a/Sources/pyOpenRPA/Orchestrator/Server.py +++ b/Sources/pyOpenRPA/Orchestrator/Server.py @@ -14,6 +14,8 @@ import json from threading import Thread import inspect +from requests import request + from pyOpenRPA.Tools import CrossOS from . import Processor # Add new processor from . import ProcessorOld # Support old processor - deprecated defs only for backward compatibility @@ -82,7 +84,7 @@ def AuthenticateBlock(inRequest): def UserAccessCheckBefore(inMethod, inRequest): # Help def - Get access flag from dict #pdb.set_trace() - global gSettingsDict + gSettingsDict = __Orchestrator__.GSettingsGet() def HelpGetFlag(inAccessRuleItem, inRequest, inGlobalDict, inAuthenticateDict): if "FlagAccess" in inAccessRuleItem: return inAccessRuleItem["FlagAccess"] @@ -171,6 +173,21 @@ def UserAccessCheckBefore(inMethod, inRequest): return lResult class HTTPRequestOld(): + mRequest:Request = None + mResponse:Response = None + OpenRPA: dict = {} + + def __init__(self,inRequest,inResponse,inAuthDict): + self.mRequest = inRequest + self.mResponse = inResponse + if inAuthDict is None: + self.OpenRPA = {} + self.OpenRPA["IsSuperToken"] = False + self.OpenRPA["AuthToken"] = None + self.OpenRPA["Domain"] = None + self.OpenRPA["User"] = None + else: self.OpenRPA = inAuthDict + # Def to check User Role access grants def UACClientCheck(self, inRoleKeyList): # Alias return self.UserRoleAccessAsk(inRoleKeyList=inRoleKeyList) @@ -205,7 +222,7 @@ class HTTPRequestOld(): #Tech def #return {"headers":[],"body":"","statuscode":111} def URLItemCheckDo(self, inURLItem, inMethod, inOnlyFlagUACBool = False): - global gSettingsDict + gSettingsDict = __Orchestrator__.GSettingsGet() ############################### #Tech sub def - do item ################################ @@ -256,7 +273,6 @@ class HTTPRequestOld(): else: inURLItem["ResponseDefRequestGlobal"]() if "ResponseFolderPath" in inURLItem: - #lRequestPath = inRequest.path lRequestPath = urllib.parse.unquote(inRequest.path) if inURLItem["URL"][-1]!="/": inURLItem["URL"]+= "/" # Fix for settings @@ -329,49 +345,35 @@ class HTTPRequestOld(): return False #ResponseContentTypeFile def SendResponseContentTypeFile(self, inContentType, inFilePath): - # Send response status code - self.send_response(200) + inResponseDict = self.OpenRPAResponseDict + self.mResponse.status_code = 200 # Send headers - self.send_header('Content-type', inContentType) + self.mResponse.headers["Content-type"]=inContentType #Check if var exist if hasattr(self, "OpenRPASetCookie"): - self.send_header("Set-Cookie", f"AuthToken={self.OpenRPA['AuthToken']}") - self.end_headers() + self.mResponse.set_cookie(key='AuthToken',value=self.OpenRPA['AuthToken']) lFileObject = open(inFilePath, "rb") # Write content as utf-8 data - self.wfile.write(lFileObject.read()) + lFileBytes = lFileObject.read() #Закрыть файловый объект lFileObject.close() + return lFileBytes # ResponseContentTypeFile def ResponseDictSend(self): - lL = gSettingsDict["Logger"] inResponseDict = self.OpenRPAResponseDict - # Send response status code - self.send_response(inResponseDict["StatusCode"]) + self.mResponse.status_code = inResponseDict["StatusCode"] # Send headers for lItemKey, lItemValue in inResponseDict["Headers"].items(): - self.send_header(lItemKey, lItemValue) + self.mResponse.headers[lItemKey]=lItemValue # Send headers: Set-Cookie for lItemKey, lItemValue in inResponseDict["SetCookies"].items(): + self.mResponse.set_cookie(key=lItemKey,value=lItemValue) self.send_header("Set-Cookie", f"{lItemKey}={lItemValue}") - #Close headers section in response - try: - self.end_headers() - # Write content as utf-8 data - self.wfile.write(inResponseDict["Body"]) - except (ConnectionResetError, ConnectionAbortedError) as e: - if lL: lL.warning(f"SERVER: Connection was forcibly closed by the client side - OK for the network interactions (ConnectionResetError: [WinError 10054] or ConnectionAbortedError: [WinError 10053])") + return inResponseDict["Body"] - def do_GET(self): + def do_GET(self, inBodyStr): try: - global gSettingsDict - #self.timeout=gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"] - self.request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"]) - threading.current_thread().request = self - self.OpenRPA = {} - self.OpenRPA["AuthToken"] = None - self.OpenRPA["Domain"] = None - self.OpenRPA["User"] = None + gSettingsDict = __Orchestrator__.GSettingsGet() self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def # Prepare result dict @@ -395,49 +397,27 @@ class HTTPRequestOld(): lFlagURLIsApplied=False lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET") if lFlagURLIsApplied: - self.ResponseDictSend() - return + return self.ResponseDictSend() #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(gSettingsDict) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) + lResponseDict = {"Headers": {'Content-type':'application/json'}, "SetCookies": {}, "Body": bytes(json.dumps(gSettingsDict), "utf8"), "StatusCode": 200} + return self.ResponseDictSend() #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 gSettingsDict["FileManager"]["FileURLFilePathDict"]: - self.SendResponseContentTypeFile('application/octet-stream', gSettingsDict["FileManager"]["FileURLFilePathDict"][lFileURL]) + return self.SendResponseContentTypeFile('application/octet-stream', gSettingsDict["FileManager"]["FileURLFilePathDict"][lFileURL]) else: - #Set access denied code - # Send response status code - self.send_response(403) - # Send headers - self.end_headers() - except BrokenPipeError as e: - lL = gSettingsDict["Logger"] - if lL: lL.warning(f"Сервер (do_GET): Возникла ошибка сети - BrokenPipeError: [Errno 32] Broken pipe. Сервер продолжает работу") + raise HTTPException(status_code=403, detail="here is the details", headers={}) except Exception as e: lL = gSettingsDict["Logger"] if lL: lL.exception(f"Сервер (do_GET): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу") # POST - def do_POST(self): + def do_POST(self, inBodyStr): try: - global gSettingsDict - #self.timeout=gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"] - self.request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"]) - threading.current_thread().request = self + gSettingsDict = __Orchestrator__.GSettingsGet() lL = gSettingsDict["Logger"] - self.OpenRPA = {} - self.OpenRPA["AuthToken"] = None - self.OpenRPA["Domain"] = None - self.OpenRPA["User"] = None self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def # Prepare result dict @@ -461,24 +441,13 @@ class HTTPRequestOld(): lFlagURLIsApplied=False lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST") if lFlagURLIsApplied: - self.ResponseDictSend() - return + return self.ResponseDictSend() #Централизованная функция получения запросов/отправки 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() + #Превращение массива байт в объект + lInputObject=json.loads(inBodyStr) # Logging info about processor activity if not SuperToken () - if not lIsSuperToken: + if not self.mOpenRPA['IsSuperToken']: lActivityTypeListStr = "" try: if type(lInputObject) is list: @@ -487,22 +456,12 @@ class HTTPRequestOld(): else: lActivityTypeListStr += f"{lInputObject['Type']}" except Exception as e: - lActivityTypeListStr = "Has some error with Activity Type read" + lActivityTypeListStr = "Обнаружена ошибка при чтении Activity Type" if lL: lL.info(f"Сервер:: !ВНИМАНИЕ! /Utils/Processor через некоторое время перестанет поддерживаться. Используйте /pyOpenRPA/Processor или /pyOpenRPA/ActivityListExecute. Активность поступила от пользователя. Домен: {self.OpenRPA['Domain']}, Логин: {self.OpenRPA['User']}, Тип активности: {lActivityTypeListStr}") - # Send message back to client - message = json.dumps(ProcessorOld.ActivityListOrDict(lInputObject)) - # Write content as utf-8 data - self.wfile.write(bytes(message, "utf8")) - return + lResponseDict = {"Headers": {'Content-type':'application/json'}, "SetCookies": {}, "Body": bytes(json.dumps(ProcessorOld.ActivityListOrDict(lInputObject)), "utf8"), "StatusCode": 200} + return self.ResponseDictSend() else: - # Send response status code - self.send_response(403) - # Send headers - self.end_headers() - return - except BrokenPipeError as e: - lL = gSettingsDict["Logger"] - if lL: lL.warning(f"Сервер, обратная совместимость (do_POST): Возникла ошибка сети - BrokenPipeError: [Errno 32] Broken pipe. Сервер продолжает работу") + raise HTTPException(status_code=403, detail="here is the details", headers={}) except Exception as e: lL = gSettingsDict["Logger"] if lL: lL.exception(f"Сервер, обратная совместимость (do_POST): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу") @@ -511,7 +470,7 @@ class HTTPRequestOld(): from typing import Union # объявление import -from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response +from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates @@ -548,12 +507,12 @@ def IdentifyAuthorize(inRequest:Request, inResponse:Response, lResult["Domain"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"] lResult["User"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"] #Set auth token - inRequest.OpenRPA={} - inRequest.OpenRPA["AuthToken"] = lCookieAuthToken - inRequest.OpenRPA["Domain"] = lResult["Domain"] - inRequest.OpenRPA["User"] = lResult["User"] - #Exit earlier - return lResult + mOpenRPA={} + mOpenRPA["AuthToken"] = lAuthToken + mOpenRPA["Domain"] = lResult["Domain"] + mOpenRPA["User"] = lResult["User"] + mOpenRPA["IsSuperToken"] = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False) + return mOpenRPA ###################################### #Way 2 - try to logon elif len(lHeaderAuthorization) == 2: @@ -579,10 +538,12 @@ def IdentifyAuthorize(inRequest:Request, inResponse:Response, gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now() #Set-cookie inResponse.set_cookie(key="AuthToken",value=lAuthToken) - inRequest.OpenRPA={} - inRequest.OpenRPA["AuthToken"] = lAuthToken - inRequest.OpenRPA["Domain"] = lResult["Domain"] - inRequest.OpenRPA["User"] = lResult["User"] + mOpenRPA={} + mOpenRPA["AuthToken"] = lAuthToken + mOpenRPA["Domain"] = lResult["Domain"] + mOpenRPA["User"] = lResult["User"] + mOpenRPA["IsSuperToken"] = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False) + return mOpenRPA #inRequest.OpenRPASetCookie = {} #New engine of server #inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken @@ -594,38 +555,47 @@ def IdentifyAuthorize(inRequest:Request, inResponse:Response, return True -def BackwardCompatibityWrapper(): # Old from v1.3.1 (updated to FastAPI) - lHTTPRequest = HTTPRequestOld() - lHTTPRequest.do_GET - lHTTPRequest.do_POST - -@app.get(path="/", response_class=PlainTextResponse) -def Hi(t:bool=Depends(IdentifyAuthorize)): - return "Hello world" +def BackwardCompatibityWrapperAuth(inRequest:Request, inResponse:Response, inBodyStr:str = Body(...), + inAuthDict:bool=Depends(IdentifyAuthorize)): # Old from v1.3.1 (updated to FastAPI) + lHTTPRequest = HTTPRequestOld(inRequest=inRequest, inResponse=inResponse, inAuthDict=inAuthDict) + threading.current_thread().request = lHTTPRequest + lResult = lHTTPRequest.do_GET(inBodyStr=inBodyStr) + if lResult is None: + lResult = lHTTPRequest.do_POST(inBodyStr=inBodyStr) + return lResult +def BackwardCompatibityWrapperNoAuth(inRequest:Request, inResponse:Response, inBodyStr:str = Body(...)): # Old from v1.3.1 (updated to FastAPI) + lHTTPRequest = HTTPRequestOld(inRequest=inRequest, inResponse=inResponse, inAuthDict=None) + threading.current_thread().request = lHTTPRequest + lResult = lHTTPRequest.do_GET(inBodyStr=inBodyStr) + if lResult is None: + lResult = lHTTPRequest.do_POST(inBodyStr=inBodyStr) + return lResult.decode("utf8") -def FastAPI(): +def InitFastAPI(): global gSettingsDict global app lL = gSettingsDict.get("Logger",None) - - - #lThreadServer = Server.RobotDaemonServer(lItemKeyStr, gSettingsDict) - #lThreadServer.start() gSettingsDict["ServerDict"]["ServerThread"] = app - app.add_api_route( - path="/", - endpoint=ServerSettings.pyOpenRPA_Index, - response_class=PlainTextResponse + for lConnectItemDict in gSettingsDict["ServerDict"]["URLList"]: + if lConnectItemDict.get("UACBool",True): + app.add_api_route( + path=lConnectItemDict["URL"], + endpoint=BackwardCompatibityWrapperAuth, + response_class=PlainTextResponse, + methods=[lConnectItemDict["Method"]] + ) + else: + app.add_api_route( + path=lConnectItemDict["URL"], + endpoint=BackwardCompatibityWrapperNoAuth, + response_class=PlainTextResponse, + methods=[lConnectItemDict["Method"]] ) - - #app.get("/", ServerSettings.pyOpenRPA_Index, response_class=PlainTextResponse) - uvicorn.run('pyOpenRPA.Orchestrator.Server:app', host='0.0.0.0', port=1024) - if lL: lL.info(f"Сервер инициализирован успешно (с поддержкой SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}, Путь к файлу сертификата (.pem): {lCertFilePathStr}") - if lL: lL.info(f"Сервер инициализирован успешно (без поддержки SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}") - + #if lL: lL.info(f"Сервер инициализирован успешно (с поддержкой SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}, Путь к файлу сертификата (.pem): {lCertFilePathStr}") + #if lL: lL.info(f"Сервер инициализирован успешно (без поддержки SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}") - if lL: lL.info("Модуль сервера FASTAPI") #Logging \ No newline at end of file + if lL: lL.info("Модуль сервера (FastAPI) инициализирован!") #Logging \ No newline at end of file