pywebexec 1.8.4__py3-none-any.whl → 1.9.0__py3-none-any.whl
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.
- pywebexec/pywebexec.py +36 -4
- pywebexec/static/css/style.css +5 -0
- pywebexec/swagger.yaml +54 -0
- pywebexec/templates/index.html +1 -1
- pywebexec/version.py +2 -2
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/METADATA +2 -1
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/RECORD +11 -10
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/LICENSE +0 -0
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/WHEEL +0 -0
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/entry_points.txt +0 -0
- {pywebexec-1.8.4.dist-info → pywebexec-1.9.0.dist-info}/top_level.txt +0 -0
pywebexec/pywebexec.py
CHANGED
@@ -28,6 +28,8 @@ import logging
|
|
28
28
|
from pathlib import Path
|
29
29
|
import pyte
|
30
30
|
from . import host_ip
|
31
|
+
from flask_swagger_ui import get_swaggerui_blueprint # new import
|
32
|
+
import yaml # new import
|
31
33
|
|
32
34
|
if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
|
33
35
|
from ldap3 import Server, Connection, ALL, SIMPLE, SUBTREE, Tls
|
@@ -35,7 +37,6 @@ if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
|
|
35
37
|
app = Flask(__name__)
|
36
38
|
app.secret_key = os.urandom(24) # Secret key for session management
|
37
39
|
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # Add SameSite attribute to session cookies
|
38
|
-
app.config['SESSION_COOKIE_SECURE'] = True
|
39
40
|
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
40
41
|
auth = HTTPBasicAuth()
|
41
42
|
|
@@ -46,6 +47,7 @@ app.config['LDAP_BASE_DN'] = os.environ.get('PYWEBEXEC_LDAP_BASE_DN')
|
|
46
47
|
app.config['LDAP_BIND_DN'] = os.environ.get('PYWEBEXEC_LDAP_BIND_DN')
|
47
48
|
app.config['LDAP_BIND_PASSWORD'] = os.environ.get('PYWEBEXEC_LDAP_BIND_PASSWORD')
|
48
49
|
|
50
|
+
|
49
51
|
# Get the Gunicorn error logger
|
50
52
|
gunicorn_logger = logging.getLogger('gunicorn.error')
|
51
53
|
app.logger.handlers = gunicorn_logger.handlers
|
@@ -590,7 +592,23 @@ def check_processes():
|
|
590
592
|
'exit_code': -1,
|
591
593
|
})
|
592
594
|
|
593
|
-
parseargs()
|
595
|
+
args = parseargs()
|
596
|
+
if args.cert:
|
597
|
+
app.config['SESSION_COOKIE_SECURE'] = True
|
598
|
+
app.config['TITLE'] = f"{args.title} API"
|
599
|
+
|
600
|
+
# Register Swagger UI blueprint with safe token included in API_URL
|
601
|
+
SWAGGER_URL = '/v0/documentation'
|
602
|
+
API_URL = '/swagger.yaml'
|
603
|
+
swaggerui_blueprint = get_swaggerui_blueprint(
|
604
|
+
SWAGGER_URL,
|
605
|
+
API_URL,
|
606
|
+
config={
|
607
|
+
'app_name': f"{args.title} API",
|
608
|
+
'layout': 'BaseLayout'
|
609
|
+
}
|
610
|
+
)
|
611
|
+
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)
|
594
612
|
|
595
613
|
def log_info(fromip, user, message):
|
596
614
|
app.logger.info(f"{user} {fromip}: {message}")
|
@@ -625,10 +643,10 @@ def stop_command(command_id):
|
|
625
643
|
|
626
644
|
@app.before_request
|
627
645
|
def check_authentication():
|
628
|
-
# Check for token in URL if TOKEN_URL is set
|
629
646
|
token = app.config.get('TOKEN_URL')
|
630
647
|
if token and request.endpoint not in ['login', 'static']:
|
631
|
-
if request.args.get('token') == token:
|
648
|
+
if request.args.get('token') == token or session.get('token') == token:
|
649
|
+
session['token'] = token
|
632
650
|
return
|
633
651
|
return jsonify({'error': 'Forbidden'}), 403
|
634
652
|
|
@@ -830,6 +848,20 @@ def do_popup(command_id):
|
|
830
848
|
</html>
|
831
849
|
"""
|
832
850
|
|
851
|
+
@app.route('/swagger.yaml')
|
852
|
+
def swagger_yaml():
|
853
|
+
swagger_path = os.path.join(os.path.dirname(__file__), 'swagger.yaml')
|
854
|
+
try:
|
855
|
+
with open(swagger_path, 'r') as f:
|
856
|
+
swagger_spec_str = f.read()
|
857
|
+
swagger_spec = yaml.safe_load(swagger_spec_str)
|
858
|
+
# Set title dynamically using the app configuration
|
859
|
+
swagger_spec['info']['title'] = app.config.get('TITLE', 'PyWebExec API')
|
860
|
+
swagger_spec_str = yaml.dump(swagger_spec)
|
861
|
+
return Response(swagger_spec_str, mimetype='application/yaml')
|
862
|
+
except Exception as e:
|
863
|
+
return Response(f"Error reading swagger spec: {e}", status=500)
|
864
|
+
|
833
865
|
def main():
|
834
866
|
global COMMAND_STATUS_DIR
|
835
867
|
basef = f"{CONFDIR}/pywebexec_{args.listen}:{args.port}"
|
pywebexec/static/css/style.css
CHANGED
@@ -95,6 +95,7 @@ form {
|
|
95
95
|
height: 30px;
|
96
96
|
background-image: url("/static/images/favicon.svg");
|
97
97
|
vertical-align: bottom;
|
98
|
+
text-indent: 35px;
|
98
99
|
}
|
99
100
|
#thStatus {
|
100
101
|
cursor: pointer;
|
@@ -383,4 +384,8 @@ span {
|
|
383
384
|
|
384
385
|
.xterm-accessibility {
|
385
386
|
display: none;
|
387
|
+
}
|
388
|
+
.swaggerlink {
|
389
|
+
text-decoration: none;
|
390
|
+
font-size: 10px;
|
386
391
|
}
|
pywebexec/swagger.yaml
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
swagger: "2.0"
|
2
|
+
info:
|
3
|
+
title: PyWebExec API
|
4
|
+
version: "1.0"
|
5
|
+
paths:
|
6
|
+
/run_command:
|
7
|
+
post:
|
8
|
+
summary: "Run a command"
|
9
|
+
consumes:
|
10
|
+
- application/json
|
11
|
+
produces:
|
12
|
+
- application/json
|
13
|
+
parameters:
|
14
|
+
- in: body
|
15
|
+
name: commandRequest
|
16
|
+
schema:
|
17
|
+
type: object
|
18
|
+
properties:
|
19
|
+
command:
|
20
|
+
type: string
|
21
|
+
params:
|
22
|
+
type: array
|
23
|
+
items:
|
24
|
+
type: string
|
25
|
+
rows:
|
26
|
+
type: integer
|
27
|
+
cols:
|
28
|
+
type: integer
|
29
|
+
responses:
|
30
|
+
"200":
|
31
|
+
description: "Command started"
|
32
|
+
/command_status/{command_id}:
|
33
|
+
get:
|
34
|
+
summary: "Get command status"
|
35
|
+
parameters:
|
36
|
+
- in: path
|
37
|
+
name: command_id
|
38
|
+
required: true
|
39
|
+
type: string
|
40
|
+
responses:
|
41
|
+
"200":
|
42
|
+
description: "Command status returned"
|
43
|
+
/commands:
|
44
|
+
get:
|
45
|
+
summary: "List commands"
|
46
|
+
responses:
|
47
|
+
"200":
|
48
|
+
description: "List of all commands"
|
49
|
+
/executables:
|
50
|
+
get:
|
51
|
+
summary: "List executables"
|
52
|
+
responses:
|
53
|
+
"200":
|
54
|
+
description: "List of executables returned as an array of executable names"
|
pywebexec/templates/index.html
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
<div id="dimmer" class="dimmer">
|
12
12
|
<div class="dimmer-text">Server not available</div>
|
13
13
|
</div>
|
14
|
-
<h2><span class="status-icon title-icon"
|
14
|
+
<h2><span class="status-icon title-icon">{{ title }} <a class="swaggerlink" href="/v0/documentation" target="_blank">swagger</a></span></h2>
|
15
15
|
<form id="launchForm" class="form-inline">
|
16
16
|
<label for="command">Command</label>
|
17
17
|
<div class="input-group">
|
pywebexec/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: pywebexec
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.9.0
|
4
4
|
Summary: Simple Python HTTP Exec Server
|
5
5
|
Home-page: https://github.com/joknarf/pywebexec
|
6
6
|
Author: Franck Jouvanceau
|
@@ -56,6 +56,7 @@ Requires-Dist: python-daemon>=2.3.2
|
|
56
56
|
Requires-Dist: cryptography>=40.0.2
|
57
57
|
Requires-Dist: Flask>=2.0.3
|
58
58
|
Requires-Dist: Flask-HTTPAuth>=4.8.0
|
59
|
+
Requires-Dist: flask-swagger-ui>=4.11.1
|
59
60
|
Requires-Dist: pexpect>=4.9.0
|
60
61
|
Requires-Dist: gunicorn>=21.2.0
|
61
62
|
Requires-Dist: ldap3>=2.9.1
|
@@ -1,8 +1,9 @@
|
|
1
1
|
pywebexec/__init__.py,sha256=197fHJy0UDBwTTpGCGortZRr-w2kTaD7MxqdbVmTEi0,61
|
2
2
|
pywebexec/host_ip.py,sha256=Ud_HTflWVQ8789aoQ2RZdT1wGI-ccvrwSWGz_c7T3TI,1241
|
3
|
-
pywebexec/pywebexec.py,sha256=
|
4
|
-
pywebexec/
|
5
|
-
pywebexec/
|
3
|
+
pywebexec/pywebexec.py,sha256=VJH7C8MI18Ybx0KHrPiBsVy17xpO4pfd_KXoNIkVBxg,33724
|
4
|
+
pywebexec/swagger.yaml,sha256=0AI4KfQI5ezTF8PP4E_ER6dfiseBi3SiOk6OUj5DjcA,1253
|
5
|
+
pywebexec/version.py,sha256=tVZQLc6Xa3Jx-HIvXI86M5yyd0Rhm5eJba4TOa0TXm0,511
|
6
|
+
pywebexec/static/css/style.css,sha256=EJ5EqaTPoMMNVwKMKVnElyX_1DnrJGVjrRbeHGGws9c,8074
|
6
7
|
pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
|
7
8
|
pywebexec/static/fonts/CommitMonoNerdFontMono-Regular.ttf,sha256=v6nZdSx5cs_TIic8Fujrjzg9u9glWjorDIr7RlwNceM,2370228
|
8
9
|
pywebexec/static/fonts/LICENSE,sha256=gsBdbFPfoMkCWYXBnjcYEAILdO0sYdUdNw8qirJQbVI,4395
|
@@ -37,11 +38,11 @@ pywebexec/static/js/xterm/addon-unicode11.js.map,sha256=paDj5KKtTIUGedQn2x7CaUTD
|
|
37
38
|
pywebexec/static/js/xterm/xterm.js,sha256=H5kaw7Syg-v5bmCuI6AKUnZd06Lkb6b92p8aqwMvdJU,289441
|
38
39
|
pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCfN0U_OI6Zw,1118392
|
39
40
|
pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
|
-
pywebexec/templates/index.html,sha256=
|
41
|
+
pywebexec/templates/index.html,sha256=VLcuC0RUkwefDugXWcXsjd5C3owKk5wCJoYIo48xbgk,3106
|
41
42
|
pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
|
42
|
-
pywebexec-1.
|
43
|
-
pywebexec-1.
|
44
|
-
pywebexec-1.
|
45
|
-
pywebexec-1.
|
46
|
-
pywebexec-1.
|
47
|
-
pywebexec-1.
|
43
|
+
pywebexec-1.9.0.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
|
44
|
+
pywebexec-1.9.0.dist-info/METADATA,sha256=FcgU6PaxvsLkhP7ppsjb6qDkmb-C7xUrwp7oK2WLSbU,8186
|
45
|
+
pywebexec-1.9.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
46
|
+
pywebexec-1.9.0.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
|
47
|
+
pywebexec-1.9.0.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
|
48
|
+
pywebexec-1.9.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|