|
|
# inRequest.OpenRPA = {}
|
|
|
# inRequest.OpenRPA["AuthToken"] = None
|
|
|
# inRequest.OpenRPA["Domain"] = None
|
|
|
# inRequest.OpenRPA["User"] = None
|
|
|
|
|
|
# lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None}
|
|
|
# self.OpenRPAResponseDict = lResponseDict
|
|
|
|
|
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
|
from socketserver import ThreadingMixIn
|
|
|
import threading
|
|
|
import json
|
|
|
from threading import Thread
|
|
|
import inspect
|
|
|
|
|
|
from pyOpenRPA.Tools import CrossOS
|
|
|
from . import Processor # Add new processor
|
|
|
from . import ProcessorOld # Support old processor - deprecated defs only for backward compatibility
|
|
|
import urllib.parse # decode URL in string
|
|
|
import importlib
|
|
|
import pdb
|
|
|
import base64
|
|
|
import uuid
|
|
|
import datetime
|
|
|
import os #for path operations
|
|
|
from http import cookies
|
|
|
global gSettingsDict
|
|
|
from . import ServerSettings
|
|
|
from . import __Orchestrator__
|
|
|
import copy
|
|
|
import mimetypes
|
|
|
mimetypes.add_type("font/woff2",".woff2")
|
|
|
mimetypes.add_type("text/javascript",".js")
|
|
|
|
|
|
gCacheDict = {}
|
|
|
|
|
|
|
|
|
# Tool to merge complex dictionaries
|
|
|
def __ComplexDictMerge2to1__(in1Dict, in2Dict):
|
|
|
lPathList=None
|
|
|
if lPathList is None: lPathList = []
|
|
|
for lKeyStr in in2Dict:
|
|
|
if lKeyStr in in1Dict:
|
|
|
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
|
|
|
__ComplexDictMerge2to1__(in1Dict[lKeyStr], in2Dict[lKeyStr])
|
|
|
elif in1Dict[lKeyStr] == in2Dict[lKeyStr]:
|
|
|
pass # same leaf value
|
|
|
else:
|
|
|
raise Exception('Conflict at %s' % '.'.join(lPathList + [str(lKeyStr)]))
|
|
|
else:
|
|
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
|
|
return in1Dict
|
|
|
|
|
|
# Tool to merge complex dictionaries - no exceptions, just overwrite dict 2 in dict 1
|
|
|
def __ComplexDictMerge2to1Overwrite__(in1Dict, in2Dict):
|
|
|
"""
|
|
|
Merge in2Dict in in1Dict. In conflict override and get value from dict 2
|
|
|
|
|
|
:param in1Dict: Source dict. Save the link (structure)
|
|
|
:param in2Dict: New data dict
|
|
|
:return: Merged dict 1
|
|
|
"""
|
|
|
lPathList=None
|
|
|
if lPathList is None: lPathList = []
|
|
|
for lKeyStr in in2Dict:
|
|
|
if lKeyStr in in1Dict:
|
|
|
if isinstance(in1Dict[lKeyStr], dict) and isinstance(in2Dict[lKeyStr], dict):
|
|
|
__ComplexDictMerge2to1Overwrite__(in1Dict[lKeyStr], in2Dict[lKeyStr])
|
|
|
else:
|
|
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
|
|
else:
|
|
|
in1Dict[lKeyStr] = in2Dict[lKeyStr]
|
|
|
return in1Dict
|
|
|
|
|
|
#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", ""))
|
|
|
global gSettingsDict
|
|
|
#pdb.set_trace()
|
|
|
if "AuthToken" in lCookies:
|
|
|
lCookieAuthToken = lCookies.get("AuthToken", "").value
|
|
|
if lCookieAuthToken:
|
|
|
#Find AuthToken in GlobalDict
|
|
|
if lCookieAuthToken in gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
|
|
|
#Auth Token Has Been Founded
|
|
|
lResult["Domain"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
|
|
|
lResult["User"] = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"]
|
|
|
#Set auth token
|
|
|
inRequest.OpenRPA["AuthToken"] = lCookieAuthToken
|
|
|
inRequest.OpenRPA["Domain"] = lResult["Domain"]
|
|
|
inRequest.OpenRPA["User"] = lResult["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 = ""
|
|
|
if "\\" in lUser:
|
|
|
lDomain = lUser.split("\\")[0]
|
|
|
lUser = lUser.split("\\")[1]
|
|
|
lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain)
|
|
|
#Check result
|
|
|
if lLogonBool:
|
|
|
lResult["Domain"] = lDomain
|
|
|
lResult["User"] = lUser
|
|
|
#Create token
|
|
|
lAuthToken=str(uuid.uuid1())
|
|
|
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
|
|
|
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
|
|
|
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
|
|
|
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False
|
|
|
gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
|
|
|
#Set-cookie
|
|
|
inRequest.OpenRPA["AuthToken"] = lAuthToken
|
|
|
inRequest.OpenRPA["Domain"] = lResult["Domain"]
|
|
|
inRequest.OpenRPA["User"] = lResult["User"]
|
|
|
inRequest.OpenRPASetCookie = {}
|
|
|
#New engine of server
|
|
|
inRequest.OpenRPAResponseDict["SetCookies"]["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"))
|
|
|
#Check access before execute the action
|
|
|
#return bool True - go execute, False - dont execute
|
|
|
def UserAccessCheckBefore(inMethod, inRequest):
|
|
|
# Help def - Get access flag from dict
|
|
|
#pdb.set_trace()
|
|
|
global gSettingsDict
|
|
|
def HelpGetFlag(inAccessRuleItem, inRequest, inGlobalDict, inAuthenticateDict):
|
|
|
if "FlagAccess" in inAccessRuleItem:
|
|
|
return inAccessRuleItem["FlagAccess"]
|
|
|
elif "FlagAccessDefRequestGlobalAuthenticate" in inAccessRuleItem:
|
|
|
return inAccessRuleItem["FlagAccessDefRequestGlobalAuthenticate"](inRequest, inGlobalDict,
|
|
|
inAuthenticateDict)
|
|
|
##########################################
|
|
|
inMethod=inMethod.upper()
|
|
|
#Prepare result false
|
|
|
lResult = False
|
|
|
lAuthToken = inRequest.OpenRPA["AuthToken"]
|
|
|
#go next if user is identified
|
|
|
lUserDict = None
|
|
|
if lAuthToken:
|
|
|
lUserDict = gSettingsDict["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]
|
|
|
#pdb.set_trace()
|
|
|
########################################
|
|
|
########################################
|
|
|
#Check general before rule (without User domain)
|
|
|
#Check rules
|
|
|
inRuleMatchURLList = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleMethodMatchURLBeforeList", [])
|
|
|
for lAccessRuleItem in inRuleMatchURLList:
|
|
|
#Go next execution if flag is false
|
|
|
if not lResult:
|
|
|
#Check if Method is identical
|
|
|
if lAccessRuleItem["Method"].upper() == inMethod:
|
|
|
#check Match type variant: BeginWith
|
|
|
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
|
|
|
lURLPath = inRequest.path
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
# check Match type variant: Contains
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
|
|
|
lURLPath = inRequest.path
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
# check Match type variant: Equal
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
|
|
|
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
# check Match type variant: EqualCase
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
|
|
|
if lAccessRuleItem["URL"] == inRequest.path:
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
#########################################
|
|
|
#########################################
|
|
|
#Do check if lResult is false
|
|
|
if not lResult:
|
|
|
#Check access by User Domain
|
|
|
#Check rules to find first appicable
|
|
|
#Check rules
|
|
|
lMethodMatchURLList = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lUserDict["Domain"].upper(), lUserDict["User"].upper()), {}).get("MethodMatchURLBeforeList", [])
|
|
|
if len(lMethodMatchURLList) > 0:
|
|
|
for lAccessRuleItem in lMethodMatchURLList:
|
|
|
#Go next execution if flag is false
|
|
|
if not lResult:
|
|
|
#Check if Method is identical
|
|
|
if lAccessRuleItem["Method"].upper() == inMethod:
|
|
|
#check Match type variant: BeginWith
|
|
|
if lAccessRuleItem["MatchType"].upper() == "BEGINWITH":
|
|
|
lURLPath = inRequest.path
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.startswith(lAccessRuleItem["URL"].upper()):
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
#check Match type variant: Contains
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "CONTAINS":
|
|
|
lURLPath = inRequest.path
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.contains(lAccessRuleItem["URL"].upper()):
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
# check Match type variant: Equal
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "EQUAL":
|
|
|
if lAccessRuleItem["URL"].upper() == inRequest.path.upper():
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
# check Match type variant: EqualCase
|
|
|
elif lAccessRuleItem["MatchType"].upper() == "EQUALCASE":
|
|
|
if lAccessRuleItem["URL"] == inRequest.path:
|
|
|
lResult = HelpGetFlag(lAccessRuleItem, inRequest, gSettingsDict, lUserDict)
|
|
|
else:
|
|
|
return True
|
|
|
#####################################
|
|
|
#####################################
|
|
|
#Return lResult
|
|
|
return lResult
|
|
|
# HTTPRequestHandler class
|
|
|
class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
|
|
|
# Def to check User Role access grants
|
|
|
def UACClientCheck(self, inRoleKeyList): # Alias
|
|
|
return self.UserRoleAccessAsk(inRoleKeyList=inRoleKeyList)
|
|
|
def UserRoleAccessAsk(self, inRoleKeyList):
|
|
|
lResult = True # Init flag
|
|
|
lRoleHierarchyDict = self.UserRoleHierarchyGet() # get the Hierarchy
|
|
|
# Try to get value from key list
|
|
|
lKeyValue = lRoleHierarchyDict # Init the base
|
|
|
for lItem in inRoleKeyList:
|
|
|
if type(lKeyValue) is dict:
|
|
|
if lItem in lKeyValue: # Has key
|
|
|
lKeyValue = lKeyValue[lItem] # Get the value and go to the next loop iteration
|
|
|
else: # Else branch - true or false
|
|
|
if len(lKeyValue)>0: # False - if Dict has some elements
|
|
|
lResult = False # Set the False Flag
|
|
|
else:
|
|
|
lResult = True # Set the True flag
|
|
|
break # Stop the loop
|
|
|
else: # Has element with no detalization - return True
|
|
|
lResult = True # Set the flag
|
|
|
break # Close the loop
|
|
|
return lResult # Return the result
|
|
|
|
|
|
# Def to get hierarchy of the current user roles
|
|
|
# if return {} - all is available
|
|
|
def UserRoleHierarchyGet(self):
|
|
|
global gSettingsDict
|
|
|
lDomainUpperStr = self.OpenRPA["Domain"].upper()
|
|
|
lUserUpperStr = self.OpenRPA["User"].upper()
|
|
|
return gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lDomainUpperStr, lUserUpperStr), {}).get("RoleHierarchyAllowedDict", {})
|
|
|
|
|
|
#Tech def
|
|
|
#return {"headers":[],"body":"","statuscode":111}
|
|
|
def URLItemCheckDo(self, inURLItem, inMethod, inOnlyFlagUACBool = False):
|
|
|
global gSettingsDict
|
|
|
###############################
|
|
|
#Tech sub def - do item
|
|
|
################################
|
|
|
def URLItemDo(inURLItem,inRequest,inGlobalDict):
|
|
|
global gCacheDict
|
|
|
inResponseDict = inRequest.OpenRPAResponseDict
|
|
|
inResponseDict["Headers"]["Content-type"]= None
|
|
|
#Set status code 200
|
|
|
inResponseDict["StatusCode"] = 200
|
|
|
#Content-type
|
|
|
if "ResponseContentType" in inURLItem:
|
|
|
inResponseDict["Headers"]["Content-type"] = inURLItem["ResponseContentType"]
|
|
|
#If file path is set
|
|
|
if "ResponseFilePath" in inURLItem:
|
|
|
# Check cache
|
|
|
if inURLItem.get("UseCacheBool",False) == True:
|
|
|
if inURLItem["ResponseFilePath"] in gCacheDict:
|
|
|
# Write content as utf-8 data
|
|
|
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
|
|
|
else:
|
|
|
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
|
|
|
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
|
|
|
# Write content as utf-8 data
|
|
|
gCacheDict[inURLItem["ResponseFilePath"]] = lFileObject.read()
|
|
|
inResponseDict["Body"] = gCacheDict[inURLItem["ResponseFilePath"]]
|
|
|
# Закрыть файловый объект
|
|
|
lFileObject.close()
|
|
|
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
|
|
else:
|
|
|
if os.path.exists(inURLItem["ResponseFilePath"]) and os.path.isfile(inURLItem["ResponseFilePath"]):
|
|
|
lFileObject = open(CrossOS.PathStr(inURLItem["ResponseFilePath"]), "rb")
|
|
|
# Write content as utf-8 data
|
|
|
inResponseDict["Body"] = lFileObject.read()
|
|
|
# Закрыть файловый объект
|
|
|
lFileObject.close()
|
|
|
else: inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
|
|
# detect MIME type if none
|
|
|
if inResponseDict["Headers"]["Content-type"] is None:
|
|
|
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(inURLItem["ResponseFilePath"])[0]
|
|
|
#If function is set
|
|
|
if "ResponseDefRequestGlobal" in inURLItem:
|
|
|
lDef = inURLItem["ResponseDefRequestGlobal"]
|
|
|
lDefSignature = inspect.signature(lDef)
|
|
|
if len(lDefSignature.parameters) == 2:
|
|
|
inURLItem["ResponseDefRequestGlobal"](inRequest, inGlobalDict)
|
|
|
elif len(lDefSignature.parameters) == 1:
|
|
|
inURLItem["ResponseDefRequestGlobal"](inRequest)
|
|
|
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
|
|
|
lFilePathSecondPart = lRequestPath.replace(inURLItem["URL"],"")
|
|
|
lFilePathSecondPart = lFilePathSecondPart.split("?")[0]
|
|
|
lFilePath = CrossOS.PathStr(os.path.join(inURLItem["ResponseFolderPath"],lFilePathSecondPart))
|
|
|
#print(f"File full path {lFilePath}")
|
|
|
#Check if file exist
|
|
|
if os.path.exists(lFilePath) and os.path.isfile(lFilePath):
|
|
|
# Check cache
|
|
|
if inURLItem.get("UseCacheBool",False) == True:
|
|
|
if lFilePath in gCacheDict:
|
|
|
# Write content as utf-8 data
|
|
|
inResponseDict["Body"] = gCacheDict[lFilePath]
|
|
|
else:
|
|
|
lFileObject = open(lFilePath, "rb")
|
|
|
# Write content as utf-8 data
|
|
|
gCacheDict[lFilePath] = lFileObject.read()
|
|
|
inResponseDict["Body"] = gCacheDict[lFilePath]
|
|
|
# Закрыть файловый объект
|
|
|
lFileObject.close()
|
|
|
else:
|
|
|
lFileObject = open(lFilePath, "rb")
|
|
|
# Write content as utf-8 data
|
|
|
inResponseDict["Body"] = lFileObject.read()
|
|
|
# Закрыть файловый объект
|
|
|
lFileObject.close()
|
|
|
# detect MIME type if none
|
|
|
if inResponseDict["Headers"]["Content-type"] is None:
|
|
|
inResponseDict["Headers"]["Content-type"]= mimetypes.guess_type(lFilePath)[0]
|
|
|
else:
|
|
|
inResponseDict["Headers"]["Content-type"]= "application/x-empty"; inResponseDict["StatusCode"] = 204 # NOCONTENT
|
|
|
# If No content-type
|
|
|
if inResponseDict["Headers"]["Content-type"] is None:
|
|
|
inResponseDict["Headers"]["Content-type"]= "application/octet-stream"
|
|
|
##############################################
|
|
|
# UAC Check
|
|
|
if inOnlyFlagUACBool == True and inURLItem.get("UACBool",None) in [None, True]:
|
|
|
return False
|
|
|
if inURLItem["Method"].upper() == inMethod.upper():
|
|
|
# check Match type variant: BeginWith
|
|
|
if inURLItem["MatchType"].upper() == "BEGINWITH":
|
|
|
lURLPath = urllib.parse.unquote(self.path)
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.startswith(inURLItem["URL"].upper()):
|
|
|
URLItemDo(inURLItem, self, gSettingsDict)
|
|
|
return True
|
|
|
# check Match type variant: Contains
|
|
|
elif inURLItem["MatchType"].upper() == "CONTAINS":
|
|
|
lURLPath = urllib.parse.unquote(self.path)
|
|
|
lURLPath = lURLPath.upper()
|
|
|
if lURLPath.contains(inURLItem["URL"].upper()):
|
|
|
URLItemDo(inURLItem, self, gSettingsDict)
|
|
|
return True
|
|
|
# check Match type variant: Equal
|
|
|
elif inURLItem["MatchType"].upper() == "EQUAL":
|
|
|
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper():
|
|
|
URLItemDo(inURLItem, self, gSettingsDict)
|
|
|
return True
|
|
|
# check Match type variant: EqualNoParam
|
|
|
elif inURLItem["MatchType"].upper() == "EQUALNOPARAM":
|
|
|
if inURLItem["URL"].upper() == urllib.parse.unquote(self.path).upper().split("?")[0]:
|
|
|
URLItemDo(inURLItem, self, gSettingsDict)
|
|
|
return True
|
|
|
# check Match type variant: EqualCase
|
|
|
elif inURLItem["MatchType"].upper() == "EQUALCASE":
|
|
|
if inURLItem["URL"] == urllib.parse.unquote(self.path):
|
|
|
URLItemDo(inURLItem, self, gSettingsDict)
|
|
|
return True
|
|
|
return False
|
|
|
#ResponseContentTypeFile
|
|
|
def SendResponseContentTypeFile(self, inContentType, inFilePath):
|
|
|
# Send response status code
|
|
|
self.send_response(200)
|
|
|
# Send headers
|
|
|
self.send_header('Content-type', inContentType)
|
|
|
#Check if var exist
|
|
|
if hasattr(self, "OpenRPASetCookie"):
|
|
|
self.send_header("Set-Cookie", f"AuthToken={self.OpenRPA['AuthToken']}")
|
|
|
self.end_headers()
|
|
|
lFileObject = open(inFilePath, "rb")
|
|
|
# Write content as utf-8 data
|
|
|
self.wfile.write(lFileObject.read())
|
|
|
#Закрыть файловый объект
|
|
|
lFileObject.close()
|
|
|
# ResponseContentTypeFile
|
|
|
def ResponseDictSend(self):
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
inResponseDict = self.OpenRPAResponseDict
|
|
|
# Send response status code
|
|
|
self.send_response(inResponseDict["StatusCode"])
|
|
|
# Send headers
|
|
|
for lItemKey, lItemValue in inResponseDict["Headers"].items():
|
|
|
self.send_header(lItemKey, lItemValue)
|
|
|
# Send headers: Set-Cookie
|
|
|
for lItemKey, lItemValue in inResponseDict["SetCookies"].items():
|
|
|
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])")
|
|
|
|
|
|
def do_GET(self):
|
|
|
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
|
|
|
self.OpenRPA["DefUserRoleAccessAsk"]=self.UserRoleAccessAsk # Alias for def
|
|
|
self.OpenRPA["DefUserRoleHierarchyGet"]=self.UserRoleHierarchyGet # Alias for def
|
|
|
# Prepare result dict
|
|
|
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None}
|
|
|
self.OpenRPAResponseDict = lResponseDict
|
|
|
############################
|
|
|
#First - all with Flag UACBool
|
|
|
############################
|
|
|
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
|
|
|
#Check if all condition are applied
|
|
|
lFlagURLIsApplied=False
|
|
|
lFlagURLIsApplied=self.URLItemCheckDo(inURLItem=lURLItem, inMethod="GET", inOnlyFlagUACBool=True)
|
|
|
if lFlagURLIsApplied:
|
|
|
self.ResponseDictSend()
|
|
|
return
|
|
|
#####################################
|
|
|
#Do authentication
|
|
|
#Check if authentication is turned on
|
|
|
#####################################
|
|
|
lFlagAccessUserBlock=False
|
|
|
lAuthenticateDict = {"Domain": "", "User": ""}
|
|
|
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
|
|
lAuthenticateDict = AuthenticateVerify(self)
|
|
|
if not lAuthenticateDict["User"]:
|
|
|
lFlagAccessUserBlock=True
|
|
|
# Logging
|
|
|
# gSettingsDict["Logger"].info(f"HTTP request /. Domain: {lAuthenticateDict['Domain']}, User: {lAuthenticateDict['User']}")
|
|
|
if lFlagAccessUserBlock:
|
|
|
AuthenticateBlock(self)
|
|
|
#####################################
|
|
|
else:
|
|
|
#Check the user access (if flag)
|
|
|
####################################
|
|
|
lFlagUserAccess = True
|
|
|
#If need user authentication
|
|
|
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
|
|
lFlagUserAccess = UserAccessCheckBefore("GET", self)
|
|
|
######################################
|
|
|
if lFlagUserAccess:
|
|
|
if CrossOS.IS_WINDOWS_BOOL: lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
|
|
|
if CrossOS.IS_LINUX_BOOL: lOrchestratorFolder = "/".join(__file__.split("/")[:-1])
|
|
|
############################
|
|
|
#New server engine (url from global dict (URLList))
|
|
|
############################
|
|
|
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
|
|
|
#Check if all condition are applied
|
|
|
lFlagURLIsApplied=False
|
|
|
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET")
|
|
|
if lFlagURLIsApplied:
|
|
|
self.ResponseDictSend()
|
|
|
return
|
|
|
#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"))
|
|
|
#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])
|
|
|
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. Сервер продолжает работу")
|
|
|
except Exception as e:
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
if lL: lL.exception(f"Сервер (do_GET): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
|
|
# POST
|
|
|
def do_POST(self):
|
|
|
try:
|
|
|
global gSettingsDict
|
|
|
self.timeout=gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"]
|
|
|
self.request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"])
|
|
|
threading.current_thread().request = self
|
|
|
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
|
|
|
#pdb.set_trace()
|
|
|
lResponseDict = {"Headers": {}, "SetCookies":{}, "Body": b"", "StatusCode": None}
|
|
|
self.OpenRPAResponseDict = lResponseDict
|
|
|
############################
|
|
|
#First - all with Flag UACBool
|
|
|
############################
|
|
|
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
|
|
|
#Check if all condition are applied
|
|
|
lFlagURLIsApplied=False
|
|
|
lFlagURLIsApplied=self.URLItemCheckDo(inURLItem=lURLItem, inMethod="POST", inOnlyFlagUACBool=True)
|
|
|
if lFlagURLIsApplied:
|
|
|
self.ResponseDictSend()
|
|
|
return
|
|
|
#####################################
|
|
|
#Do authentication
|
|
|
#Check if authentication is turned on
|
|
|
#####################################
|
|
|
lFlagAccessUserBlock=False
|
|
|
lAuthenticateDict = {"Domain": "", "User": ""}
|
|
|
lIsSuperToken = False # Is supertoken
|
|
|
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
|
|
lAuthenticateDict = AuthenticateVerify(self)
|
|
|
# Get Flag is supertoken (True|False)
|
|
|
lIsSuperToken = gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(self.OpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False)
|
|
|
if not lAuthenticateDict["User"]:
|
|
|
lFlagAccessUserBlock=True
|
|
|
if lFlagAccessUserBlock:
|
|
|
AuthenticateBlock(self)
|
|
|
#####################################
|
|
|
else:
|
|
|
#Check the user access (if flag)
|
|
|
####################################
|
|
|
lFlagUserAccess = True
|
|
|
#If need user authentication
|
|
|
if gSettingsDict.get("ServerDict", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
|
|
|
lFlagUserAccess = UserAccessCheckBefore("POST", self)
|
|
|
######################################
|
|
|
if lFlagUserAccess:
|
|
|
lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
|
|
|
############################
|
|
|
#New server engine (url from global dict (URLList))
|
|
|
############################
|
|
|
for lURLItem in gSettingsDict["ServerDict"]["URLList"]:
|
|
|
#Check if all condition are applied
|
|
|
lFlagURLIsApplied=False
|
|
|
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST")
|
|
|
if lFlagURLIsApplied:
|
|
|
self.ResponseDictSend()
|
|
|
return
|
|
|
#Централизованная функция получения запросов/отправки
|
|
|
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()
|
|
|
# Logging info about processor activity if not SuperToken ()
|
|
|
if not lIsSuperToken:
|
|
|
lActivityTypeListStr = ""
|
|
|
try:
|
|
|
if type(lInputObject) is list:
|
|
|
for lActivityItem in lInputObject:
|
|
|
lActivityTypeListStr+=f"{lActivityItem['Type']}; "
|
|
|
else:
|
|
|
lActivityTypeListStr += f"{lInputObject['Type']}"
|
|
|
except Exception as e:
|
|
|
lActivityTypeListStr = "Has some error with Activity Type read"
|
|
|
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
|
|
|
else:
|
|
|
#Set access denied code
|
|
|
# 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. Сервер продолжает работу")
|
|
|
except Exception as e:
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
if lL: lL.exception(f"Сервер (do_POST): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
|
|
#Logging
|
|
|
#!Turn it on to stop print in console
|
|
|
#def log_message(self, format, *args):
|
|
|
# return
|
|
|
|
|
|
|
|
|
|
|
|
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
|
|
daemon_threads = True
|
|
|
"""Handle requests in a separate thread."""
|
|
|
def finish_request(self, request, client_address):
|
|
|
try:
|
|
|
global gSettingsDict
|
|
|
request.settimeout(gSettingsDict["ServerDict"]["RequestTimeoutSecFloat"])
|
|
|
# "super" can not be used because BaseServer is not created from object
|
|
|
HTTPServer.finish_request(self, request, client_address)
|
|
|
except ConnectionResetError as e:
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
if lL: lL.warning(f"Сервер (finish_request): Возникла ошибка сети - ConnectionResetError: [Errno 104] Connection reset by peer. Сервер продолжает работу")
|
|
|
except Exception as e:
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
if lL: lL.exception(f"Сервер (finish_request): Неопознанная ошибка сети - см. текст ошибки. Сервер продолжает работу")
|
|
|
|
|
|
#inGlobalDict
|
|
|
# "JSONConfigurationDict":<JSON>
|
|
|
import ssl
|
|
|
class RobotDaemonServer(Thread):
|
|
|
def __init__(self,name,inGlobalDict):
|
|
|
Thread.__init__(self)
|
|
|
self.name = name
|
|
|
# Update the global dict
|
|
|
ServerSettings.SettingsUpdate(inGlobalDict)
|
|
|
def run(self):
|
|
|
global gSettingsDict
|
|
|
lL = gSettingsDict.get("Logger",None)
|
|
|
try:
|
|
|
lServerDict = gSettingsDict["ServerDict"]["ListenDict"][self.name]
|
|
|
lAddressStr=lServerDict["AddressStr"]
|
|
|
lPortInt=lServerDict["PortInt"]
|
|
|
lCertFilePathStr = lServerDict["CertFilePEMPathStr"]
|
|
|
lKeyFilePathStr = lServerDict["KeyFilePathStr"]
|
|
|
if lCertFilePathStr == "": lCertFilePathStr = None
|
|
|
if lKeyFilePathStr == "": lKeyFilePathStr = None
|
|
|
# Server settings
|
|
|
# Choose port 8080, for port 80, which is normally used for a http server, you need root access
|
|
|
server_address = (lAddressStr, lPortInt)
|
|
|
#httpd = HTTPServer(server_address, testHTTPServer_RequestHandler)
|
|
|
#httpd.serve_forever()
|
|
|
httpd = ThreadedHTTPServer(server_address, testHTTPServer_RequestHandler)
|
|
|
if lCertFilePathStr is not None:
|
|
|
if lKeyFilePathStr is not None:
|
|
|
httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile=lCertFilePathStr, keyfile=lKeyFilePathStr)
|
|
|
else:
|
|
|
httpd.socket = ssl.wrap_socket(httpd.socket, server_side=True, certfile=lCertFilePathStr)
|
|
|
if lL: lL.info(f"Сервер инициализирован успешно (с поддержкой SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}, Путь к файлу сертификата (.pem): {lCertFilePathStr}")
|
|
|
else:
|
|
|
if lL: lL.info(f"Сервер инициализирован успешно (без поддержки SSL):: Наименование: {self.name}, Слушает URL: {lAddressStr}, Слушает порт: {lPortInt}")
|
|
|
#print('Starting server, use <Ctrl-C> to stop')
|
|
|
httpd.serve_forever()
|
|
|
except Exception as e:
|
|
|
if lL: lL.exception(f"Сервер:: Ошибка при инициализации - обратитесь в тех. поддержку pyOpenRPA")
|