easy-utils-dev 2.172__tar.gz → 2.174__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/PKG-INFO +3 -1
- easy_utils_dev-2.174/easy_utils_dev/KeycloakAuthExtension.py +178 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/debugger.py +1 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/simple_sqlite.py +26 -9
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/uiserver.py +154 -13
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/wsnoclib.py +26 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev.egg-info/PKG-INFO +3 -1
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev.egg-info/SOURCES.txt +4 -2
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev.egg-info/requires.txt +2 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev.egg-info/top_level.txt +1 -0
- easy_utils_dev-2.174/playground/__init__.py +0 -0
- easy_utils_dev-2.174/playground/useapi.py +30 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/setup.py +4 -2
- easy_utils_dev-2.172/MANIFEST.in +0 -1
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/EasySsh.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/Events.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/FastQueue.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/NameObject.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/__init__.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/abortable.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/brevosmtp.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/check_license.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/cplib.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/custom_env.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/easy_oracle.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/encryptor.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/ept.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/ept_sql/create_dirs.sql +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/ept_sql/create_ept_tables.sql +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/exceptions.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/filescompressor.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/generate_license.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/keycloakapi.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/lralib.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/ne1830PSS.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/nsp_kafka.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/openid_server.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/optics_utils.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/require_auth.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/temp_memory.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/utils.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/winserviceapi.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev/wsselib.py +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/easy_utils_dev.egg-info/dependency_links.txt +0 -0
- {easy_utils_dev-2.172 → easy_utils_dev-2.174}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.174
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
@@ -19,6 +19,8 @@ Requires-Dist: pyzipper
|
|
|
19
19
|
Requires-Dist: pyjwt
|
|
20
20
|
Requires-Dist: authlib
|
|
21
21
|
Requires-Dist: kafka-python
|
|
22
|
+
Requires-Dist: cachetools
|
|
23
|
+
Requires-Dist: js2py
|
|
22
24
|
Dynamic: classifier
|
|
23
25
|
Dynamic: keywords
|
|
24
26
|
Dynamic: requires-dist
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# decorators.py
|
|
2
|
+
from functools import wraps
|
|
3
|
+
from flask import request
|
|
4
|
+
from authlib.jose import jwt, JsonWebKey
|
|
5
|
+
import requests , time , os , json
|
|
6
|
+
from cachetools import TTLCache, cached
|
|
7
|
+
from easy_utils_dev.utils import start_thread
|
|
8
|
+
from easy_utils_dev.debugger import DEBUGGER
|
|
9
|
+
from easy_utils_dev.utils import getTimestamp
|
|
10
|
+
|
|
11
|
+
class Permissions :
|
|
12
|
+
|
|
13
|
+
cache = TTLCache(maxsize=128, ttl=1800)
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
home ,
|
|
18
|
+
keycloak_url ,
|
|
19
|
+
webserver ,
|
|
20
|
+
redirect_url = None ,
|
|
21
|
+
home_url = None,
|
|
22
|
+
realm="lrm",
|
|
23
|
+
**kwargs
|
|
24
|
+
):
|
|
25
|
+
'''
|
|
26
|
+
home : the home page of the application
|
|
27
|
+
keycloak_url : the keycloak url . like http://localhost:8080
|
|
28
|
+
webserver : the webserver object
|
|
29
|
+
redirect_url : the redirect url
|
|
30
|
+
home_url : the home url
|
|
31
|
+
'''
|
|
32
|
+
self.logger = DEBUGGER(
|
|
33
|
+
name='WebServerAuth',
|
|
34
|
+
homePath=home
|
|
35
|
+
)
|
|
36
|
+
self.keycloak_url = keycloak_url
|
|
37
|
+
self.session = requests.Session()
|
|
38
|
+
self.redirect_url = redirect_url
|
|
39
|
+
self.home = home
|
|
40
|
+
self.home_url = home_url
|
|
41
|
+
self.webserver= webserver
|
|
42
|
+
self.token_refreshed_times = 5
|
|
43
|
+
self.admin_user = 'admin'
|
|
44
|
+
self.admin_password="admin"
|
|
45
|
+
self.client_secret = "yS92obzbpbk36UPsJx4lMx9EtNujkqDk"
|
|
46
|
+
self.realm = realm
|
|
47
|
+
self._thread_login_started = False
|
|
48
|
+
self.KEYCLOACKACCESSTOKEN = None
|
|
49
|
+
self.KEYCLOACKREFRESHTOKEN = None
|
|
50
|
+
self.KEYCLOACKBEARERTOKEN = None
|
|
51
|
+
start_thread(target=self._thread_login)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _admin_login(self) :
|
|
55
|
+
self.logger.info("Admin login process has started")
|
|
56
|
+
self.logger.debug("Logging in as Keycloak admin ...")
|
|
57
|
+
url = f"{self.keycloak_url}/realms/master/protocol/openid-connect/token"
|
|
58
|
+
payload=f'grant_type=password&client_id=admin-cli&username={self.admin_user}&password={self.admin_password}&client_secret={self.client_secret}&scope=openid profile'
|
|
59
|
+
headers = {
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
}
|
|
62
|
+
response = requests.request("POST", url, headers=headers, data=payload)
|
|
63
|
+
self.logger.debug(f"Admin login response status: [{response.status_code}]")
|
|
64
|
+
if response.ok :
|
|
65
|
+
self.logger.debug("Admin login successful")
|
|
66
|
+
return response.json()['access_token'] , response.json()['refresh_token']
|
|
67
|
+
self.logger.error(f"Admin login failed: {response.text}")
|
|
68
|
+
raise Exception(f"Login Failure status [{response.status_code}] error={response.text}")
|
|
69
|
+
|
|
70
|
+
def _thread_login(self) :
|
|
71
|
+
self.logger.info("Auto login/refresh process has initialized")
|
|
72
|
+
sleep_period = 1800
|
|
73
|
+
while True :
|
|
74
|
+
self.logger.debug("Auto login process is running now ...")
|
|
75
|
+
if self.token_refreshed_times >= 5 :
|
|
76
|
+
self.KEYCLOACKACCESSTOKEN , self.KEYCLOACKREFRESHTOKEN = self._admin_login()
|
|
77
|
+
self.KEYCLOACKBEARERTOKEN = f"Bearer {self.KEYCLOACKACCESSTOKEN}"
|
|
78
|
+
self.token_refreshed_times = 0
|
|
79
|
+
os.environ.setdefault('KEYCLOACKACCESSTOKEN' , self.KEYCLOACKACCESSTOKEN)
|
|
80
|
+
os.environ.setdefault('KEYCLOACKREFRESHTOKEN' , self.KEYCLOACKREFRESHTOKEN)
|
|
81
|
+
os.environ.setdefault('KEYCLOACKBEARERTOKEN' , self.KEYCLOACKBEARERTOKEN)
|
|
82
|
+
time.sleep(sleep_period)
|
|
83
|
+
continue
|
|
84
|
+
data = {
|
|
85
|
+
"client_id": 'admin-cli',
|
|
86
|
+
"grant_type": "refresh_token",
|
|
87
|
+
"refresh_token": self.KEYCLOACKREFRESHTOKEN,
|
|
88
|
+
'client_secret' : self.client_secret
|
|
89
|
+
}
|
|
90
|
+
url = f"{self.keycloak_url}/realms/master/protocol/openid-connect/token"
|
|
91
|
+
resp = requests.post(url, data=data)
|
|
92
|
+
self.logger.info(f"Admin token refreshed status [{resp.status_code}]")
|
|
93
|
+
self.KEYCLOACKACCESSTOKEN = resp.json()['access_token']
|
|
94
|
+
self.KEYCLOACKBEARERTOKEN = f"Bearer {self.KEYCLOACKACCESSTOKEN}"
|
|
95
|
+
self.KEYCLOACKREFRESHTOKEN = resp.json()['refresh_token']
|
|
96
|
+
os.environ.setdefault('KEYCLOACKACCESSTOKEN' , self.KEYCLOACKACCESSTOKEN)
|
|
97
|
+
os.environ.setdefault('KEYCLOACKREFRESHTOKEN' , self.KEYCLOACKREFRESHTOKEN)
|
|
98
|
+
os.environ.setdefault('KEYCLOACKBEARERTOKEN' , self.KEYCLOACKBEARERTOKEN)
|
|
99
|
+
self.token_refreshed_times += 1
|
|
100
|
+
time.sleep(sleep_period)
|
|
101
|
+
|
|
102
|
+
def get_jwk_set(self):
|
|
103
|
+
self.logger.debug(f"Getting JWK set for realm {self.realm}")
|
|
104
|
+
jwks_url = f"{self.keycloak_url}/realms/{self.realm}/protocol/openid-connect/certs"
|
|
105
|
+
resp = requests.get(jwks_url)
|
|
106
|
+
self.logger.debug(f'JWK.Cert.Response text: [{resp.text}]')
|
|
107
|
+
resp.raise_for_status()
|
|
108
|
+
self.logger.debug(f"JWK set for realm {self.realm} retrieved successfully")
|
|
109
|
+
return JsonWebKey.import_key_set(resp.json())
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def extract_token(self , token):
|
|
113
|
+
self.logger.debug(f'Extracting token for {token}')
|
|
114
|
+
jwks = self.get_jwk_set()
|
|
115
|
+
self.logger.debug(f'JWKS key is {jwks}')
|
|
116
|
+
claims = jwt.decode(token, key=jwks)
|
|
117
|
+
self.logger.debug(f'Extracted claims: {json.dumps(claims , indent=4)}')
|
|
118
|
+
return claims
|
|
119
|
+
|
|
120
|
+
def get_user_info(self , claims) :
|
|
121
|
+
now = getTimestamp()
|
|
122
|
+
if now > claims['exp'] :
|
|
123
|
+
return False
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
def require_roles(self , *allowed_roles):
|
|
127
|
+
def decorator(fn):
|
|
128
|
+
@wraps(fn)
|
|
129
|
+
def wrapper(*args, **kwargs):
|
|
130
|
+
self.webserver.logger.debug(f"Checking roles for function: {fn.__name__}")
|
|
131
|
+
auth = request.headers.get("Authorization", "")
|
|
132
|
+
if not auth.startswith("Bearer "):
|
|
133
|
+
self.webserver.logger.debug("No Bearer token found in Authorization header")
|
|
134
|
+
return self.webserver.Response.unauthorized(message='Authorization failed' , err="no bearer token found in Authorization header" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
135
|
+
token = auth.split(" ")[1]
|
|
136
|
+
claims = self.extract_token(token)
|
|
137
|
+
self.logger.debug(f"Extracted claims: {claims}")
|
|
138
|
+
if not claims:
|
|
139
|
+
self.webserver.logger.debug(f'Invalid or expired token. will ask client to redirect to "{self.redirect_url}"')
|
|
140
|
+
return self.webserver.Response.unauthorized(message='Invalid or expired token' , err="invalid or expired token" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
141
|
+
user_roles = claims.get("roles", [])
|
|
142
|
+
if not any(role in user_roles for role in allowed_roles):
|
|
143
|
+
self.webserver.logger.debug(f'Insufficient permissions. will ask client to redirect to "{self.redirect_url}"')
|
|
144
|
+
return self.webserver.Response.unauthorized(message='Insufficient permissions' , err="insufficient permissions" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
145
|
+
# Inject user info into request context
|
|
146
|
+
request.user_info = claims
|
|
147
|
+
self.webserver.logger.debug(f"Access granted for {fn.__name__} with roles: {user_roles}")
|
|
148
|
+
return fn(*args, **kwargs)
|
|
149
|
+
return wrapper
|
|
150
|
+
return decorator
|
|
151
|
+
|
|
152
|
+
def check_access(self):
|
|
153
|
+
def decorator(fn):
|
|
154
|
+
@wraps(fn)
|
|
155
|
+
def wrapper(*args, **kwargs):
|
|
156
|
+
self.webserver.logger.debug(f"Checking access for function: {fn.__name__}")
|
|
157
|
+
auth = request.headers.get("Authorization", "")
|
|
158
|
+
email = request.headers.get("email", "")
|
|
159
|
+
if not auth.startswith("Bearer "):
|
|
160
|
+
self.webserver.logger.debug("No Bearer token found in Authorization header")
|
|
161
|
+
return self.webserver.Response.unauthorized(message='Authorization failed' , err="no bearer token found in Authorization header" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
162
|
+
token = auth.split(" ")[1]
|
|
163
|
+
claims = self.extract_token(token)
|
|
164
|
+
if not claims :
|
|
165
|
+
self.webserver.logger.debug("Invalid or expired token. will ask client to redirect to \"{self.redirect_url}\"")
|
|
166
|
+
return self.webserver.Response.unauthorized(message='Invalid or expired token' , err="invalid or expired token" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
167
|
+
valid = self.get_user_info(claims)
|
|
168
|
+
if not valid :
|
|
169
|
+
self.webserver.logger.debug("User info validation failed for token")
|
|
170
|
+
return self.webserver.Response.unauthorized(message='Authorization failed' , err="user info validation failed for token" , home_url=self.home_url , redirect_url=self.redirect_url)
|
|
171
|
+
# Inject user info into request context
|
|
172
|
+
request.user_info = claims
|
|
173
|
+
self.webserver.logger.debug(f"Access granted for email={email}")
|
|
174
|
+
return fn(*args, **kwargs)
|
|
175
|
+
return wrapper
|
|
176
|
+
return decorator
|
|
177
|
+
|
|
178
|
+
|
|
@@ -2,7 +2,7 @@ import sqlite3 , os ,time
|
|
|
2
2
|
from .debugger import DEBUGGER
|
|
3
3
|
from .utils import getRandomKey
|
|
4
4
|
from .custom_env import custom_env , setupEnvironment
|
|
5
|
-
from .utils import fixTupleForSql
|
|
5
|
+
from .utils import fixTupleForSql, start_thread
|
|
6
6
|
|
|
7
7
|
env = custom_env()
|
|
8
8
|
|
|
@@ -25,6 +25,7 @@ class initDB :
|
|
|
25
25
|
self.lastCur = None
|
|
26
26
|
self.lastCon=None
|
|
27
27
|
self.id = id
|
|
28
|
+
self.optimized = False
|
|
28
29
|
self.WAL_JOURNAL = WAL_JOURNAL
|
|
29
30
|
self.applyDatabaseParams=True
|
|
30
31
|
if not loggerName :
|
|
@@ -49,14 +50,25 @@ class initDB :
|
|
|
49
50
|
results = tuple(list)
|
|
50
51
|
return results
|
|
51
52
|
|
|
52
|
-
def
|
|
53
|
+
def optimize_database_for_multi_threading(self) :
|
|
54
|
+
cur , con = self.db_connect(db_optimize=False)
|
|
55
|
+
cur.execute('PRAGMA journal_mode = WAL;')
|
|
56
|
+
cur.execute('PRAGMA busy_timeout = 10000;')
|
|
57
|
+
cur.execute('PRAGMA wal_autocheckpoint = 10000;')
|
|
58
|
+
cur.execute('PRAGMA temp_store = MEMORY;')
|
|
59
|
+
cur.execute('PRAGMA fullfsync = OFF;')
|
|
60
|
+
cur.execute('PRAGMA wal_checkpoint_fullfsync = OFF;')
|
|
61
|
+
con.commit()
|
|
62
|
+
con.close()
|
|
63
|
+
|
|
64
|
+
def db_connect(self, WAL=False , db_optimize = True , timeout: int=30):
|
|
53
65
|
self.logger.debug(f'connecting to {self.db_file}')
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if
|
|
57
|
-
self.
|
|
58
|
-
self.
|
|
59
|
-
return
|
|
66
|
+
con= sqlite3.connect(self.db_file , check_same_thread=False,timeout= timeout)
|
|
67
|
+
cur = con.cursor()
|
|
68
|
+
if not self.optimized and db_optimize :
|
|
69
|
+
self.optimize_database_for_multi_threading()
|
|
70
|
+
self.optimized = True
|
|
71
|
+
return cur , con
|
|
60
72
|
|
|
61
73
|
def execute_dict(self,cli:str) :
|
|
62
74
|
self.logger.debug(f"execute_dict: {cli}")
|
|
@@ -69,10 +81,15 @@ class initDB :
|
|
|
69
81
|
conn.close()
|
|
70
82
|
return h
|
|
71
83
|
|
|
72
|
-
def vacuum(self) :
|
|
84
|
+
def vacuum(self , table=None) :
|
|
73
85
|
try :
|
|
74
86
|
cur , conn = self.db_connect()
|
|
75
87
|
cur.execute('VACUUM')
|
|
88
|
+
cur.execute("PRAGMA optimize")
|
|
89
|
+
if table :
|
|
90
|
+
cur.execute(f"update sqlite_sequence set seq=0 where name='{table}'")
|
|
91
|
+
else :
|
|
92
|
+
cur.execute(f"update sqlite_sequence set seq=0")
|
|
76
93
|
conn.commit()
|
|
77
94
|
conn.close()
|
|
78
95
|
except :
|
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
import gc
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
-
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import traceback
|
|
7
|
+
import threading
|
|
8
|
+
import signal
|
|
9
|
+
import sys
|
|
5
10
|
from werkzeug.serving import ThreadedWSGIServer
|
|
6
11
|
from easy_utils_dev.utils import convertTimestampToDate, getRandomKey , generateToken , getTimestamp
|
|
7
12
|
from flask_socketio import SocketIO
|
|
8
13
|
from engineio.async_drivers import gevent
|
|
9
14
|
from engineio.async_drivers import threading as threading_engineio
|
|
10
15
|
from flask_cors import CORS
|
|
11
|
-
import
|
|
12
|
-
from flask import jsonify, request , current_app , copy_current_request_context
|
|
16
|
+
from flask import request , current_app , copy_current_request_context
|
|
13
17
|
from flask import Flask
|
|
14
18
|
from threading import Thread
|
|
15
|
-
import threading
|
|
16
19
|
from easy_utils_dev.custom_env import cenv
|
|
17
20
|
from easy_utils_dev.utils import kill_thread
|
|
18
|
-
from multiprocessing import Process
|
|
19
|
-
import traceback
|
|
20
21
|
from werkzeug.serving import make_ssl_devcert
|
|
21
22
|
from time import sleep
|
|
22
|
-
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr
|
|
23
|
+
from easy_utils_dev.utils import start_thread , getRandomKeysAndStr
|
|
23
24
|
from easy_utils_dev.temp_memory import TemporaryMemory
|
|
24
25
|
from easy_utils_dev.debugger import DEBUGGER
|
|
25
|
-
import signal
|
|
26
|
-
import sys
|
|
27
26
|
from tempfile import gettempdir
|
|
28
27
|
from urllib.parse import urlparse, parse_qs
|
|
28
|
+
from easy_utils_dev.KeycloakAuthExtension import Permissions
|
|
29
29
|
|
|
30
30
|
TMP_PATH = gettempdir()
|
|
31
31
|
|
|
32
|
-
|
|
33
32
|
def extract_buid(url: str , key ) -> str | None:
|
|
34
33
|
"""
|
|
35
34
|
Extracts the 'buid' query parameter from a URL.
|
|
@@ -224,6 +223,7 @@ class SocketClientObject :
|
|
|
224
223
|
self.browserid = None
|
|
225
224
|
|
|
226
225
|
class UISERVER :
|
|
226
|
+
|
|
227
227
|
def __init__(self ,
|
|
228
228
|
logger : DEBUGGER = None,
|
|
229
229
|
id=getRandomKey(n=15),
|
|
@@ -236,9 +236,73 @@ class UISERVER :
|
|
|
236
236
|
ssl_key=None,
|
|
237
237
|
template_folder='templates/' ,
|
|
238
238
|
static_folder = 'templates/assets',
|
|
239
|
-
socketio_async_mode = "threading"
|
|
240
|
-
|
|
239
|
+
socketio_async_mode = "threading",
|
|
240
|
+
keycloak_integration=False,
|
|
241
|
+
**kwargs
|
|
241
242
|
) -> None:
|
|
243
|
+
"""
|
|
244
|
+
Initialize the application server configuration.
|
|
245
|
+
|
|
246
|
+
Parameters
|
|
247
|
+
----------
|
|
248
|
+
logger : DEBUGGER, optional
|
|
249
|
+
Custom logger instance used for application logging.
|
|
250
|
+
If None, default logger behavior is used.
|
|
251
|
+
|
|
252
|
+
id : str, optional
|
|
253
|
+
Unique identifier for the application instance.
|
|
254
|
+
Generated automatically if not provided.
|
|
255
|
+
|
|
256
|
+
secretkey : str, optional
|
|
257
|
+
Secret key used for internal authentication, sessions,
|
|
258
|
+
or secure operations. Auto-generated by default.
|
|
259
|
+
|
|
260
|
+
serve_with_secret_key : bool, optional
|
|
261
|
+
If True, the server will require the secret key
|
|
262
|
+
for certain protected operations or endpoints.
|
|
263
|
+
|
|
264
|
+
address : str, optional
|
|
265
|
+
Host address to bind the server to.
|
|
266
|
+
Default is 'localhost'.
|
|
267
|
+
|
|
268
|
+
port : int, optional
|
|
269
|
+
Port number the server will listen on.
|
|
270
|
+
Default is 5312.
|
|
271
|
+
|
|
272
|
+
https : bool, optional
|
|
273
|
+
Enable HTTPS support.
|
|
274
|
+
If True, ssl_crt and ssl_key must be provided.
|
|
275
|
+
|
|
276
|
+
ssl_crt : str or None, optional
|
|
277
|
+
Path to the SSL certificate file (PEM/CRT).
|
|
278
|
+
Required if https=True.
|
|
279
|
+
|
|
280
|
+
ssl_key : str or None, optional
|
|
281
|
+
Path to the SSL private key file.
|
|
282
|
+
Required if https=True.
|
|
283
|
+
|
|
284
|
+
template_folder : str, optional
|
|
285
|
+
Path to the templates directory used by the web framework.
|
|
286
|
+
|
|
287
|
+
static_folder : str, optional
|
|
288
|
+
Path to the static assets directory (CSS, JS, images).
|
|
289
|
+
|
|
290
|
+
socketio_async_mode : str, optional
|
|
291
|
+
Async mode used by Socket.IO.
|
|
292
|
+
Common values: 'threading', 'eventlet', 'gevent'.
|
|
293
|
+
|
|
294
|
+
keycloak_integration : bool, optional
|
|
295
|
+
Enable Keycloak authentication integration if True.
|
|
296
|
+
|
|
297
|
+
**kwargs : dict
|
|
298
|
+
Additional keyword arguments passed to the underlying
|
|
299
|
+
framework or server configuration.
|
|
300
|
+
|
|
301
|
+
Notes
|
|
302
|
+
-----
|
|
303
|
+
- HTTPS requires valid SSL certificate and key.
|
|
304
|
+
- Secret keys should be kept secure in production environments.
|
|
305
|
+
"""
|
|
242
306
|
self.id = id
|
|
243
307
|
self.static_folder = static_folder
|
|
244
308
|
self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
|
|
@@ -289,8 +353,19 @@ class UISERVER :
|
|
|
289
353
|
name='webserver',
|
|
290
354
|
homePath=TMP_PATH
|
|
291
355
|
)
|
|
356
|
+
if keycloak_integration :
|
|
357
|
+
self.auth = Permissions(
|
|
358
|
+
home=self.logger._home ,
|
|
359
|
+
redirect_url=kwargs.get('keycloak_redirect_url') ,
|
|
360
|
+
keycloak_url=kwargs.get('keycloak_home_url') ,
|
|
361
|
+
callback_url=kwargs.get('keycloak_callback_url'),
|
|
362
|
+
webserver=self
|
|
363
|
+
)
|
|
364
|
+
else :
|
|
365
|
+
self.auth = None
|
|
292
366
|
self.start_before_request()
|
|
293
367
|
|
|
368
|
+
|
|
294
369
|
def update_cert(self , crt, ssl ) :
|
|
295
370
|
self.ssl_crt=crt
|
|
296
371
|
self.ssl_key=ssl
|
|
@@ -624,4 +699,70 @@ class UISERVER :
|
|
|
624
699
|
except:
|
|
625
700
|
pass
|
|
626
701
|
kill_thread(self.thread)
|
|
627
|
-
return True
|
|
702
|
+
return True
|
|
703
|
+
class WebServer(UISERVER):
|
|
704
|
+
def __init__(self , *args , **kwargs) :
|
|
705
|
+
"""
|
|
706
|
+
Initialize the application server configuration.
|
|
707
|
+
|
|
708
|
+
Parameters
|
|
709
|
+
----------
|
|
710
|
+
logger : DEBUGGER, optional
|
|
711
|
+
Custom logger instance used for application logging.
|
|
712
|
+
If None, default logger behavior is used.
|
|
713
|
+
|
|
714
|
+
id : str, optional
|
|
715
|
+
Unique identifier for the application instance.
|
|
716
|
+
Generated automatically if not provided.
|
|
717
|
+
|
|
718
|
+
secretkey : str, optional
|
|
719
|
+
Secret key used for internal authentication, sessions,
|
|
720
|
+
or secure operations. Auto-generated by default.
|
|
721
|
+
|
|
722
|
+
serve_with_secret_key : bool, optional
|
|
723
|
+
If True, the server will require the secret key
|
|
724
|
+
for certain protected operations or endpoints.
|
|
725
|
+
|
|
726
|
+
address : str, optional
|
|
727
|
+
Host address to bind the server to.
|
|
728
|
+
Default is 'localhost'.
|
|
729
|
+
|
|
730
|
+
port : int, optional
|
|
731
|
+
Port number the server will listen on.
|
|
732
|
+
Default is 5312.
|
|
733
|
+
|
|
734
|
+
https : bool, optional
|
|
735
|
+
Enable HTTPS support.
|
|
736
|
+
If True, ssl_crt and ssl_key must be provided.
|
|
737
|
+
|
|
738
|
+
ssl_crt : str or None, optional
|
|
739
|
+
Path to the SSL certificate file (PEM/CRT).
|
|
740
|
+
Required if https=True.
|
|
741
|
+
|
|
742
|
+
ssl_key : str or None, optional
|
|
743
|
+
Path to the SSL private key file.
|
|
744
|
+
Required if https=True.
|
|
745
|
+
|
|
746
|
+
template_folder : str, optional
|
|
747
|
+
Path to the templates directory used by the web framework.
|
|
748
|
+
|
|
749
|
+
static_folder : str, optional
|
|
750
|
+
Path to the static assets directory (CSS, JS, images).
|
|
751
|
+
|
|
752
|
+
socketio_async_mode : str, optional
|
|
753
|
+
Async mode used by Socket.IO.
|
|
754
|
+
Common values: 'threading', 'eventlet', 'gevent'.
|
|
755
|
+
|
|
756
|
+
keycloak_integration : bool, optional
|
|
757
|
+
Enable Keycloak authentication integration if True.
|
|
758
|
+
|
|
759
|
+
**kwargs : dict
|
|
760
|
+
Additional keyword arguments passed to the underlying
|
|
761
|
+
framework or server configuration.
|
|
762
|
+
|
|
763
|
+
Notes
|
|
764
|
+
-----
|
|
765
|
+
- HTTPS requires valid SSL certificate and key.
|
|
766
|
+
- Secret keys should be kept secure in production environments.
|
|
767
|
+
"""
|
|
768
|
+
super().__init__(*args , **kwargs)
|
|
@@ -133,6 +133,32 @@ class WSNOCLIB :
|
|
|
133
133
|
def getSession(self) :
|
|
134
134
|
return self.session
|
|
135
135
|
|
|
136
|
+
def getZicCardView(self,neName , return_dict=True , return_json=False ) :
|
|
137
|
+
import js2py
|
|
138
|
+
cookies = {
|
|
139
|
+
"ycsm": f"token%3DWebSessionId%3D{self.access_token}",
|
|
140
|
+
"EQMWebUITimeoutCookie": "15",
|
|
141
|
+
}
|
|
142
|
+
response = self.session.get(f"https://{self.address}:9213/{neName}/scripts/cardview.js", cookies=cookies)
|
|
143
|
+
js = originalJs = response.text
|
|
144
|
+
if len(js) < 1000 :
|
|
145
|
+
self.logger.error(f'Failed to get cardview.js. Response: {response.text}')
|
|
146
|
+
return None
|
|
147
|
+
if return_dict or return_json :
|
|
148
|
+
js = "var isOMSUser = true;" + js
|
|
149
|
+
js = js + """
|
|
150
|
+
function getData(){
|
|
151
|
+
return JSON.stringify(CARDOBJECT)
|
|
152
|
+
}"""
|
|
153
|
+
js =js.replace('new Array();', 'Object.create(null);')
|
|
154
|
+
ctx = js2py.EvalJs()
|
|
155
|
+
ctx.execute(js)
|
|
156
|
+
cards = ctx.getData()
|
|
157
|
+
if return_json :
|
|
158
|
+
return cards
|
|
159
|
+
return json.loads(cards)
|
|
160
|
+
return originalJs
|
|
161
|
+
|
|
136
162
|
def connect(self,auto_refresh_token=True) -> dict :
|
|
137
163
|
self.auto_refresh_token = auto_refresh_token
|
|
138
164
|
#refresh the session
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easy_utils_dev
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.174
|
|
4
4
|
Keywords: python3
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Requires-Dist: psutil
|
|
@@ -19,6 +19,8 @@ Requires-Dist: pyzipper
|
|
|
19
19
|
Requires-Dist: pyjwt
|
|
20
20
|
Requires-Dist: authlib
|
|
21
21
|
Requires-Dist: kafka-python
|
|
22
|
+
Requires-Dist: cachetools
|
|
23
|
+
Requires-Dist: js2py
|
|
22
24
|
Dynamic: classifier
|
|
23
25
|
Dynamic: keywords
|
|
24
26
|
Dynamic: requires-dist
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
MANIFEST.in
|
|
2
1
|
setup.py
|
|
3
2
|
easy_utils_dev/EasySsh.py
|
|
4
3
|
easy_utils_dev/Events.py
|
|
5
4
|
easy_utils_dev/FastQueue.py
|
|
5
|
+
easy_utils_dev/KeycloakAuthExtension.py
|
|
6
6
|
easy_utils_dev/NameObject.py
|
|
7
7
|
easy_utils_dev/__init__.py
|
|
8
8
|
easy_utils_dev/abortable.py
|
|
@@ -37,4 +37,6 @@ easy_utils_dev.egg-info/dependency_links.txt
|
|
|
37
37
|
easy_utils_dev.egg-info/requires.txt
|
|
38
38
|
easy_utils_dev.egg-info/top_level.txt
|
|
39
39
|
easy_utils_dev/ept_sql/create_dirs.sql
|
|
40
|
-
easy_utils_dev/ept_sql/create_ept_tables.sql
|
|
40
|
+
easy_utils_dev/ept_sql/create_ept_tables.sql
|
|
41
|
+
playground/__init__.py
|
|
42
|
+
playground/useapi.py
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Add the parent directory to sys.path to allow importing easy_utils_dev
|
|
2
|
+
import sys , os
|
|
3
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
4
|
+
from flask import request
|
|
5
|
+
from easy_utils_dev import utils
|
|
6
|
+
from easy_utils_dev.uiserver import WebServer
|
|
7
|
+
|
|
8
|
+
server = WebServer(
|
|
9
|
+
address='127.0.0.1',
|
|
10
|
+
port=3001,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
app = server.app
|
|
14
|
+
|
|
15
|
+
@app.route('/api/users' , methods=['GET'])
|
|
16
|
+
def _api_get_users():
|
|
17
|
+
return server.Response.ok(users=[{'name': 'John Doe', 'age': 30}, {'name': 'Jane Doe', 'age': 25}])
|
|
18
|
+
|
|
19
|
+
@app.route('/api/checktoken' , methods=['GET'])
|
|
20
|
+
def api_checktoken():
|
|
21
|
+
print(request.headers.get('Authorization'))
|
|
22
|
+
return server.Response.ok()
|
|
23
|
+
|
|
24
|
+
@app.route('/api/notify' , methods=['GET'])
|
|
25
|
+
def api_notify_test():
|
|
26
|
+
return server.Response.ok(toast=True , message=utils.getTimestamp())
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
server.startUi(block=True)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from setuptools import setup, find_packages
|
|
2
2
|
|
|
3
|
-
VERSION = '2.
|
|
3
|
+
VERSION = '2.174'
|
|
4
4
|
|
|
5
5
|
# Setting up
|
|
6
6
|
setup(
|
|
@@ -24,7 +24,9 @@ setup(
|
|
|
24
24
|
'pyzipper',
|
|
25
25
|
'pyjwt',
|
|
26
26
|
'authlib',
|
|
27
|
-
'kafka-python'
|
|
27
|
+
'kafka-python',
|
|
28
|
+
'cachetools',
|
|
29
|
+
'js2py'
|
|
28
30
|
],
|
|
29
31
|
keywords=['python3'],
|
|
30
32
|
classifiers=[
|
easy_utils_dev-2.172/MANIFEST.in
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
recursive-include easy_utils_dev/ept_sql *.sql
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|