diff --git a/Sources/pyOpenRPA/Orchestrator/Server.py b/Sources/pyOpenRPA/Orchestrator/Server.py index c01ff043..29fe84a3 100755 --- a/Sources/pyOpenRPA/Orchestrator/Server.py +++ b/Sources/pyOpenRPA/Orchestrator/Server.py @@ -18,9 +18,10 @@ from . import ServerBC # объявление import from fastapi import FastAPI, Form, Request, HTTPException, Depends, Header, Response, Body -from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse +from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse, RedirectResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from starlette.datastructures import MutableHeaders from pydantic import BaseModel import uvicorn import io @@ -28,6 +29,7 @@ from starlette.responses import StreamingResponse from typing import Union from pyOpenRPA import __version__ +import requests import base64 import uuid import datetime @@ -43,6 +45,8 @@ app = FastAPI( swagger_ui_oauth2_redirect_url = "/orpa/fastapi/docs/oauth2-redirect", ) + + def IdentifyAuthorize(inRequest:Request, inResponse:Response, inCookiesStr: Union[str, None] = Header(default=None,alias="Cookie"), inAuthorizationStr: Union[str, None] = Header(default="",alias="Authorization")): @@ -71,59 +75,63 @@ def IdentifyAuthorize(inRequest:Request, inResponse:Response, ###################################### #Way 2 - try to logon 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] - elif "@" in lUser: - lDomain = lUser.split("@")[1] - lUser = lUser.split("@")[0] - lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain) - #Check result - if lLogonBool: # check user in gsettings rules - lLogonBool = False - gSettings = __Orchestrator__.GSettingsGet() # Set the global settings - lUserTurple = (lDomain.upper(),lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] - lUserTurple2 = ("",lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] - if lUserTurple in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True - elif lUserTurple2 in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True - if lLogonBool: # If user exists in UAC Dict - lResult["Domain"] = lDomain - lResult["User"] = lUser - #Create token - lAuthToken=str(uuid.uuid1()) - __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {} - __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"] - __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"] - __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False - __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now() - #Set-cookie - inResponse.set_cookie(key="AuthToken",value=lAuthToken) - mOpenRPA={} - mOpenRPA["AuthToken"] = lAuthToken - mOpenRPA["Domain"] = lResult["Domain"] - mOpenRPA["User"] = lResult["User"] - mOpenRPA["IsSuperToken"] = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False) - return lAuthToken - #inRequest.OpenRPASetCookie = {} - #New engine of server - #inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken - else: - raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (для пользователя не заявлен доступ к оркестратору pyOpenRPA. Обратитесь в техническую поддержку)", headers={}) + if "AuthExc" in lCookies: + raise AuthException() else: - raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неверная пара логин / пароль)", headers={}) - ###################################### + 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] + elif "@" in lUser: + lDomain = lUser.split("@")[1] + lUser = lUser.split("@")[0] + lLogonBool = __Orchestrator__.OSCredentialsVerify(inUserStr=lUser, inPasswordStr=lPassword, inDomainStr=lDomain) + #Check result + if lLogonBool: # check user in gsettings rules + lLogonBool = False + gSettings = __Orchestrator__.GSettingsGet() # Set the global settings + lUserTurple = (lDomain.upper(),lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] + lUserTurple2 = ("",lUser.upper()) # Create turple key for inGSettings["ServerDict"]["AccessUsers"]["RuleDomainUserDict"] + if lUserTurple in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True + elif lUserTurple2 in gSettings.get("ServerDict",{}).get("AccessUsers", {}).get("RuleDomainUserDict", {}): lLogonBool = True + if lLogonBool: # If user exists in UAC Dict + lResult["Domain"] = lDomain + lResult["User"] = lUser + #Create token + lAuthToken=str(uuid.uuid1()) + __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken] = {} + __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["Domain"] = lResult["Domain"] + __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["User"] = lResult["User"] + __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["FlagDoNotExpire"] = False + __Orchestrator__.GSettingsGet()["ServerDict"]["AccessUsers"]["AuthTokensDict"][lAuthToken]["TokenDatetime"] = datetime.datetime.now() + #Set-cookie + inResponse.set_cookie(key="AuthToken",value=lAuthToken) + mOpenRPA={} + mOpenRPA["AuthToken"] = lAuthToken + mOpenRPA["Domain"] = lResult["Domain"] + mOpenRPA["User"] = lResult["User"] + mOpenRPA["IsSuperToken"] = __Orchestrator__.GSettingsGet().get("ServerDict", {}).get("AccessUsers", {}).get("AuthTokensDict", {}).get(mOpenRPA["AuthToken"], {}).get("FlagDoNotExpire", False) + try:inResponse.delete_cookie(key="AuthExc") + except Exception:pass + return lAuthToken + #inRequest.OpenRPASetCookie = {} + #New engine of server + #inRequest.OpenRPAResponseDict["SetCookies"]["AuthToken"] = lAuthToken + else: + errorMsg = "Попытка авторизации не прошла успешно (для пользователя не заявлен доступ к оркестратору pyOpenRPA. Обратитесь в техническую поддержку)" + raise ErrorException(text=errorMsg) + else: + errorMsg = "Попытка авторизации не прошла успешно (неверная пара логин / пароль)" + raise ErrorException(text=errorMsg) else: - raise HTTPException(status_code=401, detail="Попытка авторизации не прошла успешно (неполная пара логин / пароль)", headers={ 'WWW-Authenticate':'Basic'}) + raise AuthException() else: return None # Credentials are not required - return none -#network.predictor.enabled - +# Перевод встроенных fastapi функций на авторизацию lRouteList =[] for lItem in app.router.routes: lRouteList.append(lItem) @@ -137,6 +145,35 @@ for lItem in lRouteList: tags=["FastAPI"] ) +class ErrorException(Exception): + def __init__(self, text :str, name: str="AuthExc"): + self.name = name + self.text = text + +class AuthException(Exception): + def __init__(self, name: str="AuthTryWindowCreate"): + self.name = name + + +templates = Jinja2Templates(directory=CrossOS.PathJoinList(CrossOS.PathSplitList(__file__)[:-2] + ["Resources","Web","orpa"])) + +# Обработчик ошибки авторизации (вывод информации о причинах неудачной авторизации) +@app.exception_handler(ErrorException) +async def unicorn_exception_handler(request: Request, exc:ErrorException): + response = templates.TemplateResponse(status_code=401,name="badAuth.xhtml", context={"request":request, "errorMsg":exc.text, "title":"ОРКЕСТРАТОР PYOPENRPA", "subtitle":"ПАНЕЛЬ УПРАВЛЕНИЯ", "version":__version__}) + response.set_cookie(key=exc.name,value="True") + return response + +# Обработчик попытки авторизации (отвечает за вызов окна для ввода пары логин / пароль) +@app.exception_handler(AuthException) +async def unicorn_exception_handler_2(request: Request, exc: AuthException): + response = HTMLResponse(status_code=401, headers={'Content-type':'text/html; charset=utf-8', 'WWW-Authenticate':'Basic'}) + try:response.delete_cookie(key="AuthExc") + except Exception:pass + return response + + + from . import ServerSettings diff --git a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py index c74f8820..cda4106d 100755 --- a/Sources/pyOpenRPA/Orchestrator/ServerSettings.py +++ b/Sources/pyOpenRPA/Orchestrator/ServerSettings.py @@ -498,7 +498,7 @@ def SettingsUpdate(): #} #Orchestrator basic dependencies # Index page in server.py because of special settings {"Method":"GET", "URL": gSettingsDict["ServerDict"]["URLIndexStr"],"MatchType": "EqualNoParam", "ResponseDefRequestGlobal": pyOpenRPA_Index}, - {"Method":"GET", "URL": "/metadata.json", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources\\Web\\orpa\\metadata.json")), "ResponseContentType": "application/json"}, + {"Method":"GET", "URL": "/metadata.json", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources\\Web\\orpa\\metadata.json")), "ResponseContentType": "application/json","UACBool":False,}, #{"Method":"GET", "URL": "/Index.js", "MatchType": "EqualCase", "ResponseFilePath": os.path.join(lOrchestratorFolder, "Web\\Index.js"), "ResponseContentType": "text/javascript"}, {"Method":"GET", "URL": "/orpa/resources/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, CrossOS.PathStr("..\\Resources")),"UACBool":False, "UseCacheBool": True}, {"Method":"GET", "URL": "/orpa/client/resources/", "MatchType": "BeginWith", "ResponseFolderPath": os.path.join(lOrchestratorFolder, "Web"),"UACBool":False, "UseCacheBool": True}, diff --git a/Sources/pyOpenRPA/Resources/Web/orpa/badAuth.xhtml b/Sources/pyOpenRPA/Resources/Web/orpa/badAuth.xhtml new file mode 100644 index 00000000..7e8c0992 --- /dev/null +++ b/Sources/pyOpenRPA/Resources/Web/orpa/badAuth.xhtml @@ -0,0 +1,101 @@ + + + + + + + + + + + Оркестратор pyOpenRPA + + + + + + + + + + + + + + + {% include 'header.xhtml' %} +




+
+
+
Внимание
+

{{errorMsg}}

+
+ +
+ {% include 'footer.xhtml' %} + + + + + +