You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Sources/pyOpenRPA/Tools/RobotDB/Server.py

397 lines
19 KiB

from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
import threading
import json
from threading import Thread
import importlib
import pdb
import base64
import uuid
import datetime
import os #for path operations
from http import cookies
global gSettingsDict
from . import ServerSettings
#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", ""))
inRequest.OpenRPA = {}
inRequest.OpenRPA["AuthToken"] = None
#pdb.set_trace()
if "AuthToken" in lCookies:
lCookieAuthToken = lCookies.get("AuthToken", "").value
if lCookieAuthToken:
#Find AuthToken in GlobalDict
if lCookieAuthToken in gSettingsDict.get("Server", {}).get("AccessUsers", {}).get("AuthTokensDict", {}):
#Auth Token Has Been Founded
lResult["Domain"] = gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["Domain"]
lResult["User"] = gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lCookieAuthToken]["User"]
#Set auth token
inRequest.OpenRPA["AuthToken"] = lCookieAuthToken
#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]
#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())
gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {}
gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"]
gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"]
gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False
gSettingsDict["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now()
#Set-cookie
inRequest.OpenRPA["AuthToken"] = lAuthToken
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()
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["Server"]["AccessUsers"]["AuthTokensDict"][lAuthToken]
#pdb.set_trace()
########################################
########################################
#Check general before rule (without User domain)
#Check rules
for lAccessRuleItem in gSettingsDict.get("Server", {}).get("AccessUsers", {}).get("RuleMethodMatchURLBeforeList", []):
#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
for lAccessRuleItem in gSettingsDict.get("Server", {}).get("AccessUsers", {}).get("RuleDomainUserDict", {}).get((lUserDict["Domain"].upper(), lUserDict["User"].upper()), {}).get("MethodMatchURLBeforeList", []):
#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)
#####################################
#####################################
#Return lResult
return lResult
# HTTPRequestHandler class
class testHTTPServer_RequestHandler(BaseHTTPRequestHandler):
#Tech def
#return {"headers":[],"body":"","statuscode":111}
def URLItemCheckDo(self, inURLItem, inMethod):
###############################
#Tech sub def - do item
################################
def URLItemDo(inURLItem,inRequest,inGlobalDict):
inResponseDict = inRequest.OpenRPAResponseDict
#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:
lFileObject = open(inURLItem["ResponseFilePath"], "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
# Закрыть файловый объект
lFileObject.close()
#If function is set
if "ResponseDefRequestGlobal" in inURLItem:
inURLItem["ResponseDefRequestGlobal"](inRequest, inGlobalDict)
if "ResponseFolderPath" in inURLItem:
lRequestPath = inRequest.path
lFilePathSecondPart = lRequestPath.replace(inURLItem["URL"],"")
lFilePath = 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):
lFileObject = open(lFilePath, "rb")
# Write content as utf-8 data
inResponseDict["Body"] = lFileObject.read()
inResponseDict["ContentType"]= "application/octet-stream"
# Закрыть файловый объект
lFileObject.close()
##############################################
if inURLItem["Method"].upper() == inMethod.upper():
# check Match type variant: BeginWith
if inURLItem["MatchType"].upper() == "BEGINWITH":
lURLPath = 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 = 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() == self.path.upper():
URLItemDo(inURLItem, self, gSettingsDict)
return True
# check Match type variant: EqualCase
elif inURLItem["MatchType"].upper() == "EQUALCASE":
if inURLItem["URL"] == 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):
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
self.end_headers()
# Write content as utf-8 data
self.wfile.write(inResponseDict["Body"])
def do_GET(self):
# Prepare result dict
lResponseDict = {"Headers": {}, "SetCookies": {}, "Body": b"", "StatusCode": None}
self.OpenRPAResponseDict = lResponseDict
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
if gSettingsDict.get("Server", {}).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("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lFlagUserAccess = UserAccessCheckBefore("GET", self)
######################################
if lFlagUserAccess:
lOrchestratorFolder = "\\".join(__file__.split("\\")[:-1])
############################
#New server engine (url from global dict (URLList))
############################
for lURLItem in gSettingsDict["Server"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "GET")
if lFlagURLIsApplied:
self.ResponseDictSend()
return
else:
#Set access denied code
# Send response status code
self.send_response(403)
# Send headers
self.end_headers()
# POST
def do_POST(self):
# Prepare result dict
#pdb.set_trace()
lResponseDict = {"Headers": {}, "SetCookies":{}, "Body": b"", "StatusCode": None}
self.OpenRPAResponseDict = lResponseDict
#####################################
#Do authentication
#Check if authentication is turned on
#####################################
lFlagAccessUserBlock=False
lAuthenticateDict = {"Domain": "", "User": ""}
if gSettingsDict.get("Server", {}).get("AccessUsers", {}).get("FlagCredentialsAsk", False):
lAuthenticateDict = AuthenticateVerify(self)
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("Server", {}).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["Server"]["URLList"]:
#Check if all condition are applied
lFlagURLIsApplied=False
lFlagURLIsApplied=self.URLItemCheckDo(lURLItem, "POST")
if lFlagURLIsApplied:
self.ResponseDictSend()
return
return
else:
#Set access denied code
# Send response status code
self.send_response(403)
# Send headers
self.end_headers()
return
#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):
request.settimeout(30)
# "super" can not be used because BaseServer is not created from object
HTTPServer.finish_request(self, request, client_address)
#inGlobalDict
# "JSONConfigurationDict":<JSON>
class RobotDaemonServer(Thread):
def __init__(self,name,inGlobalDict):
Thread.__init__(self)
self.name = name
# Update the global dict
ServerSettings.SettingsUpdate(gSettingsDict)
def run(self):
inServerAddress="";
inPort = gSettingsDict["Server"]["ListenPort"];
# Server settings
# Choose port 8080, for port 80, which is normally used for a http server, you need root access
server_address = (inServerAddress, inPort)
#httpd = HTTPServer(server_address, testHTTPServer_RequestHandler)
# Logging
gSettingsDict["Logger"].info(f"Server init. Listen URL: {inServerAddress}, Listen port: {inPort}")
#httpd.serve_forever()
httpd = ThreadedHTTPServer(server_address, testHTTPServer_RequestHandler)
#print('Starting server, use <Ctrl-C> to stop')
httpd.serve_forever()