pywebexec 1.7.3__py3-none-any.whl → 1.7.5__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
@@ -33,6 +33,8 @@ if os.environ.get('PYWEBEXEC_LDAP_SERVER'):
33
33
  app = Flask(__name__)
34
34
  app.secret_key = os.urandom(24) # Secret key for session management
35
35
  app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # Add SameSite attribute to session cookies
36
+ app.config['SESSION_COOKIE_SECURE'] = True
37
+ app.config['SESSION_COOKIE_HTTPONLY'] = True
36
38
  auth = HTTPBasicAuth()
37
39
 
38
40
  app.config['LDAP_SERVER'] = os.environ.get('PYWEBEXEC_LDAP_SERVER')
@@ -112,7 +114,7 @@ def generate_selfsigned_cert(hostname, ip_addresses=None, key=None):
112
114
  from cryptography.hazmat.backends import default_backend
113
115
  from cryptography.hazmat.primitives import serialization
114
116
  from cryptography.hazmat.primitives.asymmetric import rsa
115
-
117
+
116
118
  # Generate our key
117
119
  if key is None:
118
120
  key = rsa.generate_private_key(
@@ -120,16 +122,16 @@ def generate_selfsigned_cert(hostname, ip_addresses=None, key=None):
120
122
  key_size=2048,
121
123
  backend=default_backend(),
122
124
  )
123
-
125
+
124
126
  name = x509.Name([
125
127
  x509.NameAttribute(NameOID.COMMON_NAME, hostname)
126
128
  ])
127
-
128
- # best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored.
129
+
130
+ # best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored.
129
131
  alt_names = [x509.DNSName(hostname)]
130
132
  alt_names.append(x509.DNSName("localhost"))
131
-
132
- # allow addressing by IP, for when you don't have real DNS (common in most testing scenarios
133
+
134
+ # allow addressing by IP, for when you don't have real DNS (common in most testing scenarios
133
135
  if ip_addresses:
134
136
  for addr in ip_addresses:
135
137
  # openssl wants DNSnames for ips...
@@ -138,7 +140,7 @@ def generate_selfsigned_cert(hostname, ip_addresses=None, key=None):
138
140
  # note: older versions of cryptography do not understand ip_address objects
139
141
  alt_names.append(x509.IPAddress(ipaddress.ip_address(addr)))
140
142
  san = x509.SubjectAlternativeName(alt_names)
141
-
143
+
142
144
  # path_len=0 means this cert can only sign itself, not other certs.
143
145
  basic_contraints = x509.BasicConstraints(ca=True, path_length=0)
144
146
  now = datetime.now(timezone.utc)
@@ -225,7 +227,7 @@ def get_last_line(file_path, cols=None, rows=None, maxsize=2048):
225
227
  except OSError:
226
228
  fd.seek(0)
227
229
  return get_visible_output(fd.read(), cols, rows)
228
-
230
+
229
231
 
230
232
  def start_gunicorn(daemonized=False, baselog=None):
231
233
  check_processes()
@@ -304,7 +306,7 @@ def daemon_d(action, pidfilepath, silent=False, hostname=None, args=None):
304
306
  def start_term():
305
307
  os.environ["PYWEBEXEC"] = " (shared)"
306
308
  os.chdir(CWD)
307
- start_time = datetime.now().isoformat()
309
+ start_time = datetime.now(timezone.utc).isoformat()
308
310
  user = pwd.getpwuid(os.getuid())[0]
309
311
  print(f"Starting terminal session for {user} : {term_command_id}")
310
312
  update_command_status(term_command_id, {
@@ -316,7 +318,7 @@ def start_term():
316
318
  })
317
319
  output_file_path = get_output_file_path(term_command_id)
318
320
  res = script(output_file_path)
319
- end_time = datetime.now().isoformat()
321
+ end_time = datetime.now(timezone.utc).isoformat()
320
322
  update_command_status(term_command_id, {
321
323
  'status': 'success',
322
324
  'end_time': end_time,
@@ -488,7 +490,7 @@ def script(output_file):
488
490
  sigwinch_passthrough(None, None)
489
491
  signal.signal(signal.SIGWINCH, sigwinch_passthrough)
490
492
  p.interact()
491
-
493
+
492
494
 
493
495
  def run_command(fromip, user, command, params, command_id):
494
496
  # app.logger.info(f'{fromip} run_command {command_id} {user}: {command} {params}')
@@ -663,7 +665,7 @@ def check_authentication():
663
665
  if request.args.get('token') == token:
664
666
  return
665
667
  return jsonify({'error': 'Forbidden'}), 403
666
-
668
+
667
669
  if not app.config['USER'] and not app.config['LDAP_SERVER']:
668
670
  return
669
671
 
@@ -704,7 +706,7 @@ def verify_ldap(username, password):
704
706
  user_dn = conn.entries[0].entry_dn
705
707
  finally:
706
708
  conn.unbind()
707
-
709
+
708
710
  # Bind with the user DN and password to verify credentials
709
711
  conn = Connection(server, user=user_dn, password=password, authentication=SIMPLE, auto_bind=True, read_only=True)
710
712
  try:
@@ -117,7 +117,7 @@ async function fetchOutput(url) {
117
117
  try {
118
118
  const response = await fetch(url);
119
119
  if (!response.ok) {
120
- document.getElementById('dimmer').style.display = 'none';
120
+ document.getElementById('dimmer').style.display = 'block';
121
121
  return;
122
122
  }
123
123
  const data = await response.json();
@@ -151,6 +151,7 @@ async function fetchOutput(url) {
151
151
  toggleButton.style.display = 'block';
152
152
  setCommandStatus(data.status)
153
153
  }
154
+ document.getElementById('dimmer').style.display = 'none';
154
155
  }
155
156
  } catch (error) {
156
157
  document.getElementById('dimmer').style.display = 'block';
@@ -361,7 +361,7 @@ async function stopCommand(command_id, event) {
361
361
  function formatTime(time) {
362
362
  if (!time || time === 'N/A') return 'N/A';
363
363
  const date = new Date(time);
364
- return date.toISOString().slice(0, 16).replace('T', ' ');
364
+ return date.toLocaleString("sv-SE", { hour12: false, timeStyle: 'short', dateStyle: 'short' }).slice(5);
365
365
  }
366
366
 
367
367
  function formatDuration(startTime, endTime) {
@@ -370,8 +370,8 @@ function formatDuration(startTime, endTime) {
370
370
  const end = new Date(endTime);
371
371
  const duration = (end - start) / 1000;
372
372
  const hours = Math.floor(duration / 3600);
373
- const minutes = Math.floor((duration % 3600) / 60);
374
- const seconds = Math.floor(duration % 60);
373
+ const minutes = Math.floor((duration % 3600) / 60).toString().padStart(2, '0');
374
+ const seconds = Math.floor(duration % 60).toString().padStart(2, '0');
375
375
  return `${hours}h ${minutes}m ${seconds}s`;
376
376
  }
377
377
 
pywebexec/version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.7.3'
16
- __version_tuple__ = version_tuple = (1, 7, 3)
15
+ __version__ = version = '1.7.5'
16
+ __version_tuple__ = version_tuple = (1, 7, 5)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: pywebexec
3
- Version: 1.7.3
3
+ Version: 1.7.5
4
4
  Summary: Simple Python HTTP Exec Server
5
5
  Home-page: https://github.com/joknarf/pywebexec
6
6
  Author: Franck Jouvanceau
@@ -153,8 +153,9 @@ $ pywebexec -u myuser [-P mypass]
153
153
  Generated password is given if no `--pasword` option
154
154
 
155
155
  * ldap(s) password check / group member
156
+ ldap server must accept memberOf attribute for group members
156
157
  ```shell
157
- $ export PYWEBEXEC_LDAP_SERVER=ldap://ldap.forumsys.com:389
158
+ $ export PYWEBEXEC_LDAP_SERVER=ldaps://ldap.mydomain.com:389
158
159
  $ export PYWEBEXEC_LDAP_BIND_DN="cn=read-only-admin,dc=example,dc=com"
159
160
  $ export PYWEBEXEC_LDAP_BIND_PASSWORD="password"
160
161
  $ export PYWEBEXEC_LDAP_BASE_DN="dc=example,dc=com"
@@ -1,6 +1,6 @@
1
1
  pywebexec/__init__.py,sha256=4spIsVaF8RJt8S58AG_wWoORRNkws9Iwqprj27C3ljM,99
2
- pywebexec/pywebexec.py,sha256=YlPtbHqISlpQN5S0gPVk0gRbvuJtg1rXrF2SPzgUXjA,33456
3
- pywebexec/version.py,sha256=Lv0gR-NbC-8DxwfmwXEmOzSq6Hgx6MH4xF1fYh_opXo,411
2
+ pywebexec/pywebexec.py,sha256=VBR6KmRiHNCd0ZvGEu_5sTqzVrUEPBhM8Hi6knS1QcA,33526
3
+ pywebexec/version.py,sha256=1PmSWdbIl6FG3GO33tTDihvotemU0-1S8uTOfaXKRIY,411
4
4
  pywebexec/static/css/Consolas NF.ttf,sha256=DJEOzF0eqZ-kxu3Gs_VE8X0NJqiobBzmxWDGpdgGRxI,1313900
5
5
  pywebexec/static/css/style.css,sha256=3s7QgbCh4wb7kfZ7Pjo-B6o3lDIBogZ-3j6AfaPdpzU,8209
6
6
  pywebexec/static/css/xterm.css,sha256=uo5phWaUiJgcz0DAzv46uoByLLbJLeetYosL1xf68rY,5559
@@ -21,8 +21,8 @@ pywebexec/static/images/resume.svg,sha256=99LP1Ya2JXakRCO9kW8JMuT_4a_CannF65Eiuw
21
21
  pywebexec/static/images/running.svg,sha256=fBCYwYb2O9K4N3waC2nURP25NRwZlqR4PbDZy6JQMww,610
22
22
  pywebexec/static/images/success.svg,sha256=NVwezvVMplt46ElW798vqGfrL21Mw_DWHUp_qiD_FU8,489
23
23
  pywebexec/static/js/commands.js,sha256=h2fkd9qpypLBxvhEEbay23nwuqUwcKJA0vHugcyL8pU,7961
24
- pywebexec/static/js/popup.js,sha256=UPKtjPLIR5KaAV2i1iWUGwcCTX1tT3vnOIMc8VKQqag,9311
25
- pywebexec/static/js/script.js,sha256=yO2PEsyO1F5w3lSCJyKkAtp21Tb7I3MQMPibxvdmFOc,18198
24
+ pywebexec/static/js/popup.js,sha256=oVkwnOKHae11omBiTZs-00NVeS9HGNEiUo71JSwOqn4,9382
25
+ pywebexec/static/js/script.js,sha256=TtniIzVgYa9A0nXPS13MpcGyGZfrM-r8eb0pmpqgXJA,18301
26
26
  pywebexec/static/js/xterm/LICENSE,sha256=EU1P4eXTull-_T9I80VuwnJXubB-zLzUl3xpEYj2T1M,1083
27
27
  pywebexec/static/js/xterm/addon-canvas.js,sha256=ez6QTVvsmLVNJmdJlM-ZQ5bErwlxAQ_9DUmDIptl2TM,94607
28
28
  pywebexec/static/js/xterm/addon-canvas.js.map,sha256=ECBA4B-BqUpdFeRzlsEWLSQnudnhLP-yPQJ8_hKquMo,379537
@@ -35,9 +35,9 @@ pywebexec/static/js/xterm/xterm.js.map,sha256=Y7O2Pb-fIS7Z8AC1D5s04_aiW_Jf1f4mCf
35
35
  pywebexec/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  pywebexec/templates/index.html,sha256=2fEN8cggHBEd8-RamDFpnekVJtIbRembFSw0-1YEptc,2979
37
37
  pywebexec/templates/popup.html,sha256=GT2jY7oOxpCaBaRl924QJWdFBmfSOP952T13d37R_pY,1506
38
- pywebexec-1.7.3.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
39
- pywebexec-1.7.3.dist-info/METADATA,sha256=-JC0TqKo6F5B43R4TKYjxzoyXvL986W-djlTshi6dUM,8060
40
- pywebexec-1.7.3.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
- pywebexec-1.7.3.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
42
- pywebexec-1.7.3.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
43
- pywebexec-1.7.3.dist-info/RECORD,,
38
+ pywebexec-1.7.5.dist-info/LICENSE,sha256=gRJf0JPT_wsZJsUGlWPTS8Vypfl9vQ1qjp6sNbKykuA,1064
39
+ pywebexec-1.7.5.dist-info/METADATA,sha256=ueE1L7xvCOkQEjAF480gyouw1IiRKk0tVOUahe1dxU4,8122
40
+ pywebexec-1.7.5.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
+ pywebexec-1.7.5.dist-info/entry_points.txt,sha256=l52GBkPCXRkmlHfEyoVauyfBdg8o-CAtC8qQpOIjJK0,55
42
+ pywebexec-1.7.5.dist-info/top_level.txt,sha256=vHoHyzngrfGdm_nM7Xn_5iLmaCrf10XO1EhldgNLEQ8,10
43
+ pywebexec-1.7.5.dist-info/RECORD,,