pywebexec 1.8.4__py3-none-any.whl → 1.9.1__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 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}"
@@ -91,10 +91,13 @@ form {
91
91
  vertical-align: middle;
92
92
  }
93
93
  .title-icon {
94
- width: 30px;
94
+ display: inline;
95
95
  height: 30px;
96
96
  background-image: url("/static/images/favicon.svg");
97
+ background-size: 30px 30px;
98
+ background-repeat: no-repeat;
97
99
  vertical-align: bottom;
100
+ padding-left: 35px;
98
101
  }
99
102
  #thStatus {
100
103
  cursor: pointer;
@@ -383,4 +386,8 @@ span {
383
386
 
384
387
  .xterm-accessibility {
385
388
  display: none;
389
+ }
390
+ .swaggerlink {
391
+ text-decoration: none;
392
+ font-size: 10px;
386
393
  }
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"
@@ -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"></span>{{ title }}</h2>
14
+ <h2><span class="status-icon title-icon">{{ title }}&nbsp;<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
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '1.8.4'
21
- __version_tuple__ = version_tuple = (1, 8, 4)
20
+ __version__ = version = '1.9.1'
21
+ __version_tuple__ = version_tuple = (1, 9, 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.8.4
3
+ Version: 1.9.1
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=Pwsjq9VpM19l10-suEZ5nEXBv-LOaJNZy91ubuAkBos,32541
4
- pywebexec/version.py,sha256=p_0hDBrc9f-T772HiFHRsyrcnjBKkPgfVRYmFuXiLTo,511
5
- pywebexec/static/css/style.css,sha256=0hnO7QUqms5XMPkD_veYzcy1FcThuG8Lo13FRkotv-0,7986
3
+ pywebexec/pywebexec.py,sha256=VJH7C8MI18Ybx0KHrPiBsVy17xpO4pfd_KXoNIkVBxg,33724
4
+ pywebexec/swagger.yaml,sha256=0AI4KfQI5ezTF8PP4E_ER6dfiseBi3SiOk6OUj5DjcA,1253
5
+ pywebexec/version.py,sha256=0TTXzdumH9yE7lSKUOdvHb1HJBaZX7fxyxuZNWj70Cg,511
6
+ pywebexec/static/css/style.css,sha256=SuOU_USRh8BiAxEJ1LDYIx3asf3lETu_evWzA54gsBo,8145
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=Hqranqut5gaZrY3nWmfRmVPQgcfnkV1Jndppkj4PK30,3025
41
+ pywebexec/templates/index.html,sha256=VLcuC0RUkwefDugXWcXsjd5C3owKk5wCJoYIo48xbgk,3106
41
42
  pywebexec/templates/popup.html,sha256=3kpMccKD_OLLhJ4Y9KRw6Ny8wQWjVaRrUfV9y5-bDiQ,1580
42
- pywebexec-1.8.4.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
43
- pywebexec-1.8.4.dist-info/METADATA,sha256=6ybiOn5xV8duhm7D_QiV7R413JWzUi64ZmACjT4Asv0,8146
44
- pywebexec-1.8.4.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
45
- pywebexec-1.8.4.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
46
- pywebexec-1.8.4.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
47
- pywebexec-1.8.4.dist-info/RECORD,,
43
+ pywebexec-1.9.1.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
44
+ pywebexec-1.9.1.dist-info/METADATA,sha256=xo0Wz-wbREaii01oK9ro9ObeOWIIV4Zf40_5a74EGkI,8186
45
+ pywebexec-1.9.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
46
+ pywebexec-1.9.1.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
47
+ pywebexec-1.9.1.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
48
+ pywebexec-1.9.1.dist-info/RECORD,,