From a0e85bb79558167d6dc7bf703566cd0b2913f97d Mon Sep 17 00:00:00 2001 From: Ivan Maslov Date: Sun, 22 Nov 2020 01:06:13 +0300 Subject: [PATCH] dev in progress - need update for GUID -do not create it +- Add absolute/relative import for the control panels +- Add new Orchestrator defs: +- - def OrchestratorAccessUserUpdate(inGSettings, inADLoginStr, inADStr="", inADIsDefaultBool=True, inURLList=[], inCPAllowKeyList=[]): - Update user access +- - def OrchestratorAccessSuperTokenAdd(inGSettings, inSuperTokenStr): # Add supertoken for the all access (it is need for the robot communication without human) --- .../CP_VersionCheck.py | 0 Orchestrator/OrchestratorSettings.py | 47 +++ .../HowToConfigureOrchestrator.txt | 0 Orchestrator/{ => Readme}/ProcessorTypes.txt | 0 Orchestrator/Settings/AccessUser_IMaslov.py | 32 -- Orchestrator/Settings/AccessUser_ND.py | 32 -- .../Settings/AccessUser_SuperToken.py | 40 -- .../Settings/SettingsOrchestratorExample.py | 390 ------------------ Orchestrator/Settings/__init__.py | 0 Orchestrator/pyOpenRPA.Orchestrator_x64.cmd | 6 +- ...Orchestrator_x64_administrator_startup.cmd | 2 +- .../pyOpenRPA_Orchestrator.exe | Bin 0 -> 99856 bytes .../pyOpenRPA/Orchestrator/Orchestrator.py | 38 ++ changelog.md | 4 + 14 files changed, 93 insertions(+), 498 deletions(-) rename Orchestrator/{Settings => ControlPanel}/CP_VersionCheck.py (100%) create mode 100644 Orchestrator/OrchestratorSettings.py rename Orchestrator/{ => Readme}/HowToConfigureOrchestrator.txt (100%) rename Orchestrator/{ => Readme}/ProcessorTypes.txt (100%) delete mode 100644 Orchestrator/Settings/AccessUser_IMaslov.py delete mode 100644 Orchestrator/Settings/AccessUser_ND.py delete mode 100644 Orchestrator/Settings/AccessUser_SuperToken.py delete mode 100644 Orchestrator/Settings/SettingsOrchestratorExample.py delete mode 100644 Orchestrator/Settings/__init__.py create mode 100644 Resources/WPy64-3720/python-3.7.2.amd64/pyOpenRPA_Orchestrator.exe diff --git a/Orchestrator/Settings/CP_VersionCheck.py b/Orchestrator/ControlPanel/CP_VersionCheck.py similarity index 100% rename from Orchestrator/Settings/CP_VersionCheck.py rename to Orchestrator/ControlPanel/CP_VersionCheck.py diff --git a/Orchestrator/OrchestratorSettings.py b/Orchestrator/OrchestratorSettings.py new file mode 100644 index 00000000..c7e3d7b9 --- /dev/null +++ b/Orchestrator/OrchestratorSettings.py @@ -0,0 +1,47 @@ +import psutil, datetime, logging, os, sys # stdout from logging + +# Config settings +lPyOpenRPASourceFolderPathStr = r"..\Sources" # Path for test pyOpenRPA package + +# INFO Relative/Absolute import see below - after settings init + +# Template for import CP - Control Panels +# ATTENTION - Pay attention to CP names! Orchestrator is one for the all control panels per one machine + +## !!! For Absolute import !!! +# sys.path.insert(0,os.path.abspath(os.path.join(r"..\ROBOT\Builds"))) +# import pyRobot_CP +# pyRobot_CP.SettingsUpdate(inGSettings=gSettings) + +## !!! For Relative import !!! +# sys.path.insert(0,os.path.abspath(os.path.join(r"..\ROBOT\Builds"))) +# from pyRobot_CP import ControlPanel +# ControlPanel.SettingsUpdate(inGSettings=gSettings) + +# Operations +if lPyOpenRPASourceFolderPathStr != "": sys.path.insert(0,os.path.abspath(os.path.join(lPyOpenRPASourceFolderPathStr))) # Path for test pyOpenRPA package +# Start import after config the pyOpenRPA folder +from pyOpenRPA.Orchestrator import SettingsTemplate # Import functionallity + +if __name__ == "__main__": # New init way - allow run as module -m PyOpenRPA.Orchestrator + from pyOpenRPA.Orchestrator import Orchestrator # Import orchestrator main + gSettings = SettingsTemplate.Create(inModeStr="BASIC") # Create GSettings with basic configuration + + # TEST Add User ND - Add Login ND to superuser of the Orchestrator + Orchestrator.OrchestratorAccessUserUpdate(inGSettings=gSettings, inADLoginStr="ND", inADStr="", inADIsDefaultBool=True, inURLList=[],inCPAllowKeyList=[]) + # TEST Add User IMaslov - Add Login IMaslov to superuser of the Orchestrator + Orchestrator.OrchestratorAccessUserUpdate(inGSettings=gSettings, inADLoginStr="IMaslov", inADStr="", inADIsDefaultBool=True, inURLList=[],inCPAllowKeyList=[]) + # TEST Add Supertoken for the all access between robots + Orchestrator.OrchestratorAccessSuperTokenAdd(inGSettings=gSettings, inSuperTokenStr="1992-04-03-0643-ru-b4ff-openrpa52zzz") + + ## !!! For Relative import !!! CP Version Check + sys.path.insert(0,os.path.abspath(os.path.join(r""))) + from ControlPanel import CP_VersionCheck + CP_VersionCheck.SettingsUpdate(inGSettings=gSettings) + + # Call the orchestrator def + Orchestrator.Orchestrator(inGSettings=gSettings) + +else: + print("!WARNING! Current orchestrator settings do not support old type of the Orchestrator start. Use new Orchestrator type start (see v1.2.0)") + diff --git a/Orchestrator/HowToConfigureOrchestrator.txt b/Orchestrator/Readme/HowToConfigureOrchestrator.txt similarity index 100% rename from Orchestrator/HowToConfigureOrchestrator.txt rename to Orchestrator/Readme/HowToConfigureOrchestrator.txt diff --git a/Orchestrator/ProcessorTypes.txt b/Orchestrator/Readme/ProcessorTypes.txt similarity index 100% rename from Orchestrator/ProcessorTypes.txt rename to Orchestrator/Readme/ProcessorTypes.txt diff --git a/Orchestrator/Settings/AccessUser_IMaslov.py b/Orchestrator/Settings/AccessUser_IMaslov.py deleted file mode 100644 index f3dc2507..00000000 --- a/Orchestrator/Settings/AccessUser_IMaslov.py +++ /dev/null @@ -1,32 +0,0 @@ -# Init Section -gUserNameStr = "IMaslov" # User name -gDomainNameStr = "" # DOMAIN or EMPTY str -gDomainIsDefaultBool = True # If domain is exist and is default (default = you can type login without domain name) - -def SettingsUpdate(inDict): - lRuleDomainUserDict = { - "MethodMatchURLBeforeList": [ - { - "Method":"GET", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - }, - { - "Method":"POST", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - } - ], - "ControlPanelKeyAllowedList": ["TestControlPanel", "RobotRDPActive","RobotScreenActive", "ControlPanel_Template"] # If empty - all is allowed - } - # Case add domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({(gDomainNameStr.upper(),gUserNameStr.upper()):lRuleDomainUserDict}) - if gDomainIsDefaultBool: - # Case add default domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({("",gUserNameStr.upper()):lRuleDomainUserDict}) - #Return current dict - return inDict \ No newline at end of file diff --git a/Orchestrator/Settings/AccessUser_ND.py b/Orchestrator/Settings/AccessUser_ND.py deleted file mode 100644 index 7f359fc7..00000000 --- a/Orchestrator/Settings/AccessUser_ND.py +++ /dev/null @@ -1,32 +0,0 @@ -# Init Section -gUserNameStr = "ND" # User name -gDomainNameStr = "" # DOMAIN or EMPTY str -gDomainIsDefaultBool = True # If domain is exist and is default (default = you can type login without domain name) - -def SettingsUpdate(inDict): - lRuleDomainUserDict = { - "MethodMatchURLBeforeList": [ - { - "Method":"GET", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - }, - { - "Method":"POST", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - } - ], - "ControlPanelKeyAllowedList": ["TestControlPanel", "RobotRDPActive","RobotScreenActive", "ControlPanel_Template"] # If empty - all is allowed - } - # Case add domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({(gDomainNameStr.upper(),gUserNameStr.upper()):lRuleDomainUserDict}) - if gDomainIsDefaultBool: - # Case add default domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({("",gUserNameStr.upper()):lRuleDomainUserDict}) - #Return current dict - return inDict \ No newline at end of file diff --git a/Orchestrator/Settings/AccessUser_SuperToken.py b/Orchestrator/Settings/AccessUser_SuperToken.py deleted file mode 100644 index 16674c0e..00000000 --- a/Orchestrator/Settings/AccessUser_SuperToken.py +++ /dev/null @@ -1,40 +0,0 @@ -import datetime -# Init Section -gUserNameStr = "SUPERTOKEN" # User name -gDomainNameStr = "" # DOMAIN or EMPTY str -gDomainIsDefaultBool = False # If domain is exist and is default (default = you can type login without domain name) -gAuthTokenPermanentStr = "1992-04-03-0643-ru-b4ff-openrpa52zzz" # Set the permanent Auth token if it is needed, or empty -def SettingsUpdate(inDict): - lRuleDomainUserDict = { - "MethodMatchURLBeforeList": [ - { - "Method":"GET", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - }, - { - "Method":"POST", - "MatchType":"Beginwith", - "URL":"/", - #"FlagAccessDefRequestGlobalAuthenticate": TestDef - "FlagAccess": True - } - ], - "ControlPanelKeyAllowedList": ["TestControlPanel", "RobotRDPActive","RobotScreenActive", "ControlPanel_Template"] # If empty - all is allowed - } - # Case add domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({(gDomainNameStr.upper(),gUserNameStr.upper()):lRuleDomainUserDict}) - if gDomainIsDefaultBool: - # Case add default domain + user - inDict["Server"]["AccessUsers"]["RuleDomainUserDict"].update({("",gUserNameStr.upper()):lRuleDomainUserDict}) - #"":{"User":"", "Domain":"", "TokenDatetime":} - #!!!!!!!!!!!!!!!!!!!!!!! - #Attention: default supertoken is 1992-04-03-0643-ru-b4ff-openrpa52zzz - please change it when you will customize OpenRPA in your company - #!!!!!!!!!!!!!!!!!!!!!!! - inDict["Server"]["AccessUsers"]["AuthTokensDict"].update( - {gAuthTokenPermanentStr:{"User":gUserNameStr, "Domain":gDomainNameStr, "TokenDatetime": datetime.datetime.now(), "FlagDoNotExpire":True}} - ) - #Return current dict - return inDict \ No newline at end of file diff --git a/Orchestrator/Settings/SettingsOrchestratorExample.py b/Orchestrator/Settings/SettingsOrchestratorExample.py deleted file mode 100644 index 813e09df..00000000 --- a/Orchestrator/Settings/SettingsOrchestratorExample.py +++ /dev/null @@ -1,390 +0,0 @@ -import psutil -import datetime -import logging -import sys # stdout fro logging -import os -# Init variables -gControlPanelPyFilePathList = [ # .py file path list for call SettingsUpdate - #r"" -] - -def RenderRobotR01(inGlobalConfiguration): - #Subheader Variants - lSubheaderRunTrueText="Состояние: Работает" - lSubheaderRunFalseText="Состояние: Не работает" - #Run button - #Такое большое количество слэшей связано с тем, что этот текст отправляется сначала в браузер, рендерится там, а потом отправляется на процессор оркестратора - lOnClickRunButton="""mGlobal.Controller.CMDRunText('C:\\test.png');""" - #Force close button - lOnClickForceCloseButton="""mGlobal.Controller.CMDRunText('taskkill /F /im Robot_R01.exe');""" - #Result template - lResultDict={ - "HeaderLeftText":"Автозагрузка заявок на расход", - "HeaderRightText":"R01", - "DataStorageKey":"R01_Data", #Use key for set current dict in mGlobal.DataStorage["DtaaStorageKey"] on client side - "SubheaderText":lSubheaderRunFalseText, - "BodyKeyValueList":[ - #Дата запуска: 10:00:09 01.09.2019 - {"Key":"Дата запуска","Value":"Не запущен"}, - {"Key":"Текущий шаг","Value":"Не запущен"}, - {"Key":"Время выполнения шага","Value":"--с."}, - {"Key":"Отчет робота","Value":"Скачать"} - ], - "FooterText":"Дата изменения: 9:38:00 09.10.2019", - "FooterButtonX2List":[ - {"Text":"Ручной запуск", "Color":"green", "Link":"", "OnClick": lOnClickRunButton.replace("\\","\\\\")}, - {"Text":"Безопасная остановка", "Color disabled":"orange", "Link":""} - ], - "FooterButtonX1List":[ - {"Text":"Принудительная остановка", "Color":"red", "Link":"", "OnClick": lOnClickForceCloseButton.replace("\\","\\\\")} - ] - } - #Check if process running - if CheckIfProcessRunning("Robot_R01"): - lResultDict["SubheaderText"]=lSubheaderRunTrueText - #Fill robot info from Storage - R01 - if "Robot_R01" in inGlobalConfiguration.get("Storage",{}): - lItemDict=inGlobalConfiguration["Storage"]["Robot_R01"] - #lResultDict["HeaderLeftText"]=lItemDict["Name"] - #lResultDict["HeaderRightText"]=lItemDict["Code"] - lResultDict["BodyKeyValueList"][0]["Value"]=lItemDict.get("RunDateTimeString","Не запущен") - lResultDict["BodyKeyValueList"][1]["Value"]=lItemDict.get("StepCurrentName","Не запущен") - lResultDict["BodyKeyValueList"][2]["Value"]=lItemDict.get("StepCurrentDuration","--с.") - else: - #Process not running - lResultDict["FooterText"]=f'Дата изменения: {datetime.datetime.now().strftime("%H:%M:%S %d.%m.%Y")}' - else: - #Process not running - lResultDict["FooterText"]=f'Дата изменения: {datetime.datetime.now().strftime("%H:%M:%S %d.%m.%Y")}' - return lResultDict - -def CheckIfProcessRunning(processName): - ''' - Check if there is any running process that contains the given name processName. - ''' - #Iterate over the all the running process - for proc in psutil.process_iter(): - try: - # Check if process name contains the given name string. - if processName.lower() in proc.name().lower(): - return True - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - return False; - -#Orchestrator settings -def Settings(): - import os - import pyOpenRPA.Orchestrator - lOrchestratorFolder = "\\".join(pyOpenRPA.Orchestrator.__file__.split("\\")[:-1]) - mDict = { - "Autocleaner": { # Some gurbage is collecting in g settings. So you can configure autocleaner to periodically clear gSettings - "IntervalSecFloat": 600.0, # Sec float to periodically clear gsettings - }, - "Client":{ # Settings about client web orchestrator - "Session":{ # Settings about web session. Session algorythms works only for special requests (URL in ServerSettings) - "LifetimeSecFloat": 600.0, # Client Session lifetime in seconds. after this time server will forget about this client session - "LifetimeRequestSecFloat": 120.0, # 1 client request lifetime in server in seconds - "ControlPanelRefreshIntervalSecFloat": 1.5, # Interval to refresh control panels for session, - "TechnicalSessionGUIDCache": { # TEchnical cache. Fills when web browser is requesting - #"SessionGUIDStr":{ # Session with some GUID str. On client session guid stored in cookie "SessionGUIDStr" - # "InitDatetime": None, # Datetime when session GUID was created - # "DatasetLast": { - # "ControlPanel": { - # "Data": None, # Struct to check with new iterations. None if starts - # "ReturnBool": False # flag to return, close request and return data as json - # } - # }, - # "ClientRequestHandler": None, # Last client request handler - # "UserADStr": None, # User, who connect. None if user is not exists - # "DomainADStr": None, # Domain of the user who connect. None if user is not exists - #} - } - } - }, - "Server": { - "WorkingDirectoryPathStr": None , # Will be filled automatically - "RequestTimeoutSecFloat": 300, # Time to handle request in seconds - "ListenPort_": "Порт, по которому можно подключиться к демону", - "ListenPort": 80, - "ListenURLList": [ - { - "Description": "Local machine test", - "URL_": "Сетевое расположение сервера демона", - "URL": "" - } - ], - "AccessUsers": { #Default - all URL is blocked - "FlagCredentialsAsk": True, #Turn on Authentication - "RuleDomainUserDict": { - #("DOMAIN", "USER"): { !!!!! only in upper case !!!! - # "MethodMatchURLBeforeList": [ - # { - # "Method":"GET|POST", - # "MatchType":"BeginWith|Contains|Equal|EqualCase", - # "URL":"", - # "FlagAccessDefRequestGlobalAuthenticate": None, #Return bool - # "FlagAccess": True - # } - # ], - # "ControlPanelKeyAllowedList":[], # If empty - all is allowed - # "RoleHierarchyAllowedDict": { - # "Orchestrator":{ - # "Controls": { - # "RestartOrchestrator": {}, # Feature to restart orchestrator on virtual machine - # "LookMachineScreenshots": {} # Feature to look machina screenshots - # }, - # "RDPActive": { # Robot RDP active module - # "ListRead": {} # Access to read RDP session list - # } - # } - # } - # } - }, - "RuleMethodMatchURLBeforeList": [ #General MethodMatchURL list (no domain/user) - # { - # "Method":"GET|POST", - # "MatchType":"BeginWith|Contains|Equal|EqualCase", - # "URL":"", - # "FlagAccessDefRequestGlobalAuthenticate": None, #Return bool - # "FlagAccess": True - # } - ], - "AuthTokensDict": { - #"":{"User":"", "Domain":"", "TokenDatetime":, "FlagDoNotExpire":True} - } - }, - "URLList":[ #List of available URLs with the orchestrator server - #{ - # "Method":"GET|POST", - # "URL": "/index", #URL of the request - # "MatchType": "", #"BeginWith|Contains|Equal|EqualCase", - # "ResponseFilePath": "", #Absolute or relative path - # "ResponseFolderPath": "", #Absolute or relative path - # "ResponseContentType": "", #HTTP Content-type - # "ResponseDefRequestGlobal": None #Function with str result - #} - { - "Method":"GET", - "URL": "/test/", #URL of the request - "MatchType": "BeginWith", #"BeginWith|Contains|Equal|EqualCase", - #"ResponseFilePath": "", #Absolute or relative path - "ResponseFolderPath": "C:\Abs\Archive\scopeSrcUL\OpenRPA\Orchestrator\Settings", #Absolute or relative path - #"ResponseContentType": "", #HTTP Content-type - #"ResponseDefRequestGlobal": None #Function with str result - } - ] - }, - "OrchestratorStart":{ - "DefSettingsUpdatePathList":[], # List of the .py files which should be loaded before init the algorythms - "ActivityList":[ - #{ - # "Type": "ProcessStop", #Activity type - # "Name": "OpenRPARobotDaemon.exe", #Process name - # "FlagForce": True, #Force process close - # "User": "%username%" #Empty, user or %username% - #}, - #{ - # "Type": "ProcessStartIfTurnedOff", #Activity type - # "CheckTaskName": "notepad.exe", #Python function module name - # "Path": "notepad", #Python function name - # "ArgList": [] #Input python function args - #}, - # { - # "Type": "RDPSessionConnect", #Activity type - start/connect RDP Session - # "RDPSessionKeyStr": "notepad.exe", #Python function module name - # "RDPConfigurationDict": {} - # }, - # { - # "Type": "RDPSessionLogoff", #Activity type - logoff RDP Session - # "RDPSessionKeyStr": "notepad.exe", #Python function module name - # }, - # { - # "Type": "RDPSessionDisconnect", #Activity type - disconnect the RDP Session without logoff - # "RDPSessionKeyStr": "notepad.exe", #Python function module name - # }, - # { - # "Type": "RDPSessionFileSend", #Activity type - send file to RDP session - # ... - # }, - # { - # "Type": "RDPSessionFileRecieve", #Activity type - recieve file from rdp session - # ... - # }, - # { - # "Type": "RDPSessionProcessStart", #Activity type - - # ... - # }, - ] - }, - "Scheduler": { - "ActivityTimeCheckLoopSeconds":5, #Количество секунд, между циклами проверки действий - "ActivityTimeList": [ - { - "TimeHH:MM": "22:23", #Time [HH:MM] to trigger activity - "WeekdayList": [1,2,3,4,5,6,7], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] - "Activity":{ - "Type": "ProcessStart", #Activity type - "Path": "start", #Executable file path - "ArgList": ["cmd.exe","/c","PIPUpgrade.cmd"] #List of the arguments - }, - "GUID": None #Will be fied in Orchestrator automatically - is needed for detect activity completion - }, - { - "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% - }, - "GUID": None #Will be fied in Orchestrator automatically - is needed for detect activity completion - }, - { - "TimeHH:MMStart": "12:40", #Time [HH:MM] to trigger activity - "TimeHH:MMStop": "12:40", - "ActivityIntervalSeconds": 2, - "WeekdayList": [1, 2, 3, 4, 5, 6, 7], #List of the weekday index when activity is applicable, Default [1,2,3,4,5,6,7] - "Activity":{ - "Type": "ProcessStartIfTurnedOff", #Activity type - "CheckTaskName": "notepad.exe", #Python function module name - "Path": "notepad", #Python function name - "ArgList": [] #Input python function args - }, - "GUID": None #Will be fied in Orchestrator automatically - is needed for detect activity completion - } - ] - }, - "ProcessorDict": { # Has been changed. New general processor (one threaded) v.1.2.0 - "ActivityList": [ # List of the activities - # { - # "Def":"DefAliasTest", # def link or def alias (look gSettings["Processor"]["AliasDefDict"]) - # "ArgList":[1,2,3], # Args list - # "ArgDict":{"ttt":1,"222":2,"dsd":3} # Args dictionary - # "ArgGSettings": # Name of GSettings attribute: str (ArgDict) or index (for ArgList) - # }, - ], - "AliasDefDict": {}, # Storage for def with Str alias. To use it see pyOpenRPA.Orchestrator.ControlPanel - "CheckIntervalSecFloat": 1.0, # Interval for check gSettings in ProcessorDict > ActivityList - "ExecuteBool": True, # Flag to execute thread processor - "ThreadIdInt": None # Technical field - will be setup when processor init - }, - "ControlPanelDict": { - "RefreshSeconds": 5, # deprecated parameter - "RobotList": [ - { - "RenderFunction": RenderRobotR01, - "KeyStr":"TestControlPanelKey" - } - ] - }, - # # # # # # # # # # # # # # - "RobotRDPActive":{ - "RDPList": { - #"RDPSessionKey":{ - # "Host": "77.77.22.22", # Host address - # "Port": "3389", # RDP Port - # "Login": "test", # Login - # "Password": "test", # Password - # "Screen": { - # "Width": 1680, # Width of the remote desktop in pixels - # "Height": 1050, # Height of the remote desktop in pixels - # # "640x480" or "1680x1050" or "FullScreen". If Resolution not exists set full screen - # "FlagUseAllMonitors": False, # True or False - # "DepthBit": "32" # "32" or "24" or "16" or "15" - # }, - # "SharedDriveList": ["c"], # List of the Root sesion hard drives - # ###### Will updated in program ############ - # "SessionHex": "", # Hex is created when robot runs - # "SessionIsWindowExistBool": False, # Flag if the RDP window is exist, old name "FlagSessionIsActive". Check every n seconds - # "SessionIsWindowResponsibleBool": False, # Flag if RDP window is responsible (recieve commands). Check every nn seconds. If window is Responsible - window is Exist too - # "SessionIsIgnoredBool": False # Flag to ignore RDP window False - dont ignore, True - ignore - #} - }, - "ResponsibilityCheckIntervalSec": None, - # Seconds interval when Robot check the RDP responsibility. if None - dont check - "FullScreenRDPSessionKeyStr": None, # RDPSessionKeyStr of the current session which is full screened, None is no session in fullscreen - "ActivityList":[ # Technical Activity list for RobotRDPActive thread - equal to Main activity list, apply only RDP activity - # { - # "DefNameStr":"test", # Function name in RobotRDPActive.Processor - # "ArgList":[1,2,3], # Args list - # "ArgDict":{"ttt":1,"222":2,"dsd":3} # Args dictionary - # }, - #{ - # "DefNameStr": "RDPSessionConnect", # Function name in RobotRDPActive.Processor - # "ArgList": [], # Args list - # "ArgDict": {"inRDPSessionKeyStr": "TestRDP", "inHostStr": "77.44.33.22", "inPortStr": "3389", - # "inLoginStr": "login", "inPasswordStr": "pass"} # Args dictionary - #}, - # { - # "DefNameStr": "RDPSessionDisconnect", # Disconnect the RDP session without logoff. Function name in RobotRDPActive.Processor - # "ArgList": [], # Args list - # "ArgDict": {"inRDPSessionKeyStr": "TestRDP"} - # }, - # { - # "DefNameStr": "RDPSessionReconnect", # Disconnect the RDP session without logoff. Function name in RobotRDPActive.Processor - # "ArgList": [], # Args list - # "ArgDict": {"inRDPSessionKeyStr": "TestRDP"} - # } - ] - }, - # # # # # # # # # # # # # # - "FileManager": { - "FileURLFilePathDict_help": "https://localhost:8081/filemanager/. All FileURL s must be set in lowercase", - "FileURLFilePathDict": { - "r01/report.xlsx": "C:\\RPA\\R01_IntegrationOrderOut\\Data\\Reestr_otgruzok.xlsx" - } - }, - "Logger": logging.getLogger("Orchestrator"), - "Storage": { - "Robot_R01_help": "Robot data storage in orchestrator env", - "Robot_R01": {}, - "R01_OrchestratorToRobot":{"Test2":"Test2"} - } - } - #Создать файл логирования - # add filemode="w" to overwrite - if not os.path.exists("Reports"): - os.makedirs("Reports") - ########################## - #Подготовка логгера Robot - ######################### - mRobotLogger=mDict["Logger"] - mRobotLogger.setLevel(logging.INFO) - # create the logging file handler - mRobotLoggerFH = logging.FileHandler("Reports\ReportOrchestrator_"+datetime.datetime.now().strftime("%Y_%m_%d")+".log") - mRobotLoggerFormatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - mRobotLoggerFH.setFormatter(mRobotLoggerFormatter) - # add handler to logger object - mRobotLogger.addHandler(mRobotLoggerFH) - ####################Add console output - handler = logging.StreamHandler(sys.stdout) - handler.setFormatter(mRobotLoggerFormatter) - mRobotLogger.addHandler(handler) - ############################################ - ################################### - #Init .py files from Settings folder - #################################### - #Get file list from Settings folder - #lFunction to call in subfiles - lSubmoduleFunctionName = "SettingsUpdate" - #lSettingsPath = os.path.join(inSettingsFolderPath, "Settings") - lSettingsPath = "\\".join(os.path.join(os.getcwd(),__file__).split("\\")[:-1]) - #lSettingsPath = os.path.join(os.getcwd(), "Settings") - #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 os.path.join(lSettingsPath, f) != __file__] - import importlib.util - for lModuleFilePathItem in lFileList + gControlPanelPyFilePathList: # UPD 2020 04 27 Add gControlPanelPyFilePathList to import py files from Robots - try: # Try to init - go next if error and log in logger - lModuleName = lModuleFilePathItem[0:-3] - lFileFullPath = os.path.join(lSettingsPath, lModuleFilePathItem) - lTechSpecification = importlib.util.spec_from_file_location(lModuleName, lFileFullPath) - lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification) - lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec) - if lSubmoduleFunctionName in dir(lTechModuleFromSpec): - #Run SettingUpdate function in submodule - getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(mDict) - except Exception as e: - if mRobotLogger: mRobotLogger.exception(f"Error when init .py file in orchestrator '{lModuleFilePathItem}'. Exception is below:") - return mDict \ No newline at end of file diff --git a/Orchestrator/Settings/__init__.py b/Orchestrator/Settings/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd b/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd index e13afe15..ddb14a81 100644 --- a/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd +++ b/Orchestrator/pyOpenRPA.Orchestrator_x64.cmd @@ -1,4 +1,4 @@ -cd %~dp0\..\Sources -copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\OpenRPA_Orchestrator.exe -.\..\Resources\WPy64-3720\python-3.7.2.amd64\OpenRPA_Orchestrator.exe -m pyOpenRPA.Orchestrator "..\Orchestrator\Settings\SettingsOrchestratorExample.py" +cd %~dp0 +copy /Y ..\Resources\WPy64-3720\python-3.7.2.amd64\python.exe ..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Orchestrator.exe +.\..\Resources\WPy64-3720\python-3.7.2.amd64\pyOpenRPA_Orchestrator.exe "OrchestratorSettings.py" pause >nul \ No newline at end of file diff --git a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd b/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd index 186aa981..2b98f0ea 100644 --- a/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd +++ b/Orchestrator/pyOpenRPA.Orchestrator_x64_administrator_startup.cmd @@ -1,2 +1,2 @@ cd %~dp0 -start pyOpenRPA.Orchestrator_x64_shortcut_admin.lnk \ No newline at end of file +start pyOpenRPA.Orchestrator_x64_administrator.lnk \ No newline at end of file diff --git a/Resources/WPy64-3720/python-3.7.2.amd64/pyOpenRPA_Orchestrator.exe b/Resources/WPy64-3720/python-3.7.2.amd64/pyOpenRPA_Orchestrator.exe new file mode 100644 index 0000000000000000000000000000000000000000..66a6265ce8c6d564afeaad63d75bbe97fe045230 GIT binary patch literal 99856 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

L?vYr|Q^&u?6qVxh(g2Uc6&X)q0r7 zv(@_h?XANro2V5NmsAQF&1dp+9?X23sdSEL(Hw2dxS31gYTojyQ2KJ7)r0Q|oyA^N z472`~RT)M>!zEsp+@3`beRNMvG=K2YOsSx}Es-X9>dv$ld|h&L{6YGpaa`K!p|=`E zrrSqvB?U}B^2bN;ojwymtXs+LASrZF70w4vuL@fZ7rM3tJEtgp&NkxY@B3a2P*6PPN!2Q(O_t*!mO)6<>BNvtCV>kCJri% z-MBGnY?VjdzYTxreO>z%)wpvjrkuo*gu=hkNj5IgQU;Y}hy(LWPGUrJQ1iu#OV1L^ z3>L?=h%pxM>TezLC}pM%KfUBAi*rsvU4XvLeo*}D07-BC#Z~vl&ob7_2M57(|8 zuDUr%*@{zbm($y|Y|3M6(%G2KXKPI0m>9p8sZ!3RNz#HV{>ypV+9SwDu=7xBp>wC&P6=JZ@N8%94npP({PH`6opVc zhr>#INJ9_4CD(+B+Pd7ExKFvNIQ5_=!>_POByOp@AXQSip76D{bTmU7ftED&^}Tgx z-Uv>IG)eqgFf{#P{AGl3rQA*K64%Ag1oaiC?rNpfHSUVrIwqJ$Dmn>hDTYxq{as150^Hb(L8i?J&qCskKc&PiK7G9tHHN0-XGL`xD< z_B{2zGBEbg^t5y7BRd1J_oM3%U)E?cwOZ``eO)tBfA{%XZv&I5j%U=Gv-YWa?=spD z-jyX9BuR7g&xTh!4U+Hd%j42_s&hjp7D<_Dx_#zW;hA8rY~8b!mSxz?Wm6m4f&&QN>0TW) zNko+_Q|Hx|@%5d4YRHp;fgj*9!#$rDC(2zs!A$(ASSxpK-oZoTyReb<(oef5^WvjO zQne}RBd^Q%hr6#x2{DU%Jt*zDaXyd4K{!Pg9%A;K&Mth2y)bia;;>4z{`|bO#H6yx zlMPEp!)p5R_y~Sv*e#)u-+{L(*h~{ieETYn{^`xGsjH<-#l~kR3qBQ-D3j*rs98}d zYVG)EGyZw_BnfJ*N2-JLMxn9gDGl!?-w&&|`snv>@cfdyVTNqUyM5|$ze(w>{HSL) z)@Ndl4=+)WPT|RSe+r+14n3Gyvt5R|qi5~T_YTio;d^lDSG<|vg5aHhg?^6xxk+or zmUkj;a?6znfsXo!P0K8YX!awQ^7% zN)`0mlsL9WdOXAWP{qgU<1gBSH(0?IeDeL1A5RxF2f{{mVC|**;qmFUFYn`HO$?Bv z_dQAj<+t>lb#i`P`FdoN=QbOA5#+DEIs8kjv^}P-Aa0n6t$%R|#HsH2ijS1qMYV69 z$LxF}rdp^x=H#6CXi?<3)&+`V(S4>K8+S4FA50PQtQmha7QSs-eOlHzP~u0uCVq0LL!gk+efy4n z)x^^se61Mms}l`-FmINXxh;BaSJ)S}<~CMQ*aJdAzRiJdd7F_E{nf9-t0%@UrT%LE z9IttQhMk#Io0dBR$JG-lSMomN*Z&w`6mxAvOKLD z9b4IAwl|Fb(6z*$=FH7|N8jD^mWS|j`a9+Vu1(1VC;a@!2yIep1mAHfGi%kf#KF

#x7%*StZ3k^01N)pU!# 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 literal 0 HcmV?d00001 diff --git a/Sources/pyOpenRPA/Orchestrator/Orchestrator.py b/Sources/pyOpenRPA/Orchestrator/Orchestrator.py index a7527947..b18fbbff 100644 --- a/Sources/pyOpenRPA/Orchestrator/Orchestrator.py +++ b/Sources/pyOpenRPA/Orchestrator/Orchestrator.py @@ -61,6 +61,44 @@ def OrchestratorSessionSave(inGSettings=None): ## Orchestrator session save if lL: lL.info( f"Orchestrator has dump the RDP list before the restart. The RDP List is {inGSettings['RobotRDPActive']['RDPList']}") return True +# Update user access +def OrchestratorAccessUserUpdate(inGSettings, inADLoginStr, inADStr="", inADIsDefaultBool=True, inURLList=[], inCPAllowKeyList=[]): + # Backward compatibility + if inURLList==[]: + inURLList=[ + { + "Method": "GET", + "MatchType": "Beginwith", + "URL": "/", + # "FlagAccessDefRequestGlobalAuthenticate": TestDef + "FlagAccess": True + }, + { + "Method": "POST", + "MatchType": "Beginwith", + "URL": "/", + # "FlagAccessDefRequestGlobalAuthenticate": TestDef + "FlagAccess": True + } + ] + # Create Access item + lRuleDomainUserDict = { + "MethodMatchURLBeforeList": inURLList, + "ControlPanelKeyAllowedList": inCPAllowKeyList, #["TestControlPanel", "RobotRDPActive","RobotScreenActive", "ControlPanel_Template"] # If empty - all is allowed + } + # Case add domain + user + inGSettings["Server"]["AccessUsers"]["RuleDomainUserDict"].update({(inADStr.upper(),inADLoginStr.upper()):lRuleDomainUserDict}) + if inADIsDefaultBool: + # Case add default domain + user + inGSettings["Server"]["AccessUsers"]["RuleDomainUserDict"].update({("",inADLoginStr.upper()):lRuleDomainUserDict}) + +# Add supertoken for the all access (it is need for the robot communication without human) +def OrchestratorAccessSuperTokenAdd(inGSettings, inSuperTokenStr): + lLoginStr = "SUPERTOKEN" + OrchestratorAccessUserUpdate(inGSettings=inGSettings, inADLoginStr=lLoginStr) + inGSettings["Server"]["AccessUsers"]["AuthTokensDict"].update( + {inSuperTokenStr:{"User":lLoginStr, "Domain":"", "TokenDatetime": datetime.datetime.now(), "FlagDoNotExpire":True}} + ) ## GSettings defs def GSettingsKeyListValueSet(inGSettings, inValue, inKeyList=[]): # Set value in GSettings by the key list diff --git a/changelog.md b/changelog.md index 3c7dee7b..d73ee49f 100644 --- a/changelog.md +++ b/changelog.md @@ -25,6 +25,10 @@ - Orchestrator WEB fix: Don't request screenshot if no modal is active in front - Add Version field in Orchestrator back + front - Add front autorefresh if front/back pyOpenRPA versions are differs (see CP_VersionCheck.py) +- Add absolute/relative import for the control panels +- Add new Orchestrator defs: +- - def OrchestratorAccessUserUpdate(inGSettings, inADLoginStr, inADStr="", inADIsDefaultBool=True, inURLList=[], inCPAllowKeyList=[]): - Update user access +- - def OrchestratorAccessSuperTokenAdd(inGSettings, inSuperTokenStr): # Add supertoken for the all access (it is need for the robot communication without human) [1.1.0] After 2 month test prefinal with new improovements (+RobotRDPActive in Orchestrator + Easy ControlPanelTemplate) Beta before 1.1.0 (new way of OpenRPA with improvements. Sorry, but no backward compatibility)/ Backward compatibility will start from 1.0.1