2023-11-03 06:25:24 -05:00
|
|
|
##########################################################################
|
|
|
|
#
|
|
|
|
# pgAdmin 4 - PostgreSQL Tools
|
|
|
|
#
|
2024-01-01 02:43:48 -06:00
|
|
|
# Copyright (C) 2013 - 2024, The pgAdmin Development Team
|
2023-11-03 06:25:24 -05:00
|
|
|
# This software is released under the PostgreSQL License
|
|
|
|
#
|
|
|
|
##########################################################################
|
2022-10-15 04:19:04 -05:00
|
|
|
import logging
|
|
|
|
import subprocess
|
2023-11-03 06:25:24 -05:00
|
|
|
from datetime import datetime, timedelta, timezone
|
2022-10-15 04:19:04 -05:00
|
|
|
from threading import Lock
|
|
|
|
|
|
|
|
from flask import current_app
|
|
|
|
|
|
|
|
import config
|
|
|
|
|
|
|
|
|
|
|
|
class PasswordExec:
|
|
|
|
|
|
|
|
lock = Lock()
|
|
|
|
|
|
|
|
def __init__(self, cmd, expiration_seconds=None, timeout=60):
|
|
|
|
self.cmd = str(cmd)
|
|
|
|
self.expiration_seconds = int(expiration_seconds) \
|
|
|
|
if expiration_seconds is not None else None
|
|
|
|
self.timeout = int(timeout)
|
|
|
|
self.password = None
|
|
|
|
self.last_result = None
|
|
|
|
|
|
|
|
def get(self):
|
2024-02-23 02:35:26 -06:00
|
|
|
if config.SERVER_MODE and not config.ENABLE_SERVER_PASS_EXEC_CMD:
|
2022-10-15 04:19:04 -05:00
|
|
|
# Arbitrary shell execution on server is a security risk
|
|
|
|
raise NotImplementedError('Passexec not available in server mode')
|
|
|
|
with self.lock:
|
|
|
|
if not self.password or self.is_expired():
|
|
|
|
if not self.cmd:
|
|
|
|
return None
|
2022-11-18 22:43:41 -06:00
|
|
|
current_app.logger.info('Calling passexec')
|
2023-11-03 06:25:24 -05:00
|
|
|
now = datetime.now(timezone.utc)
|
2022-10-15 04:19:04 -05:00
|
|
|
try:
|
|
|
|
p = subprocess.run(
|
|
|
|
self.cmd,
|
|
|
|
shell=True,
|
|
|
|
timeout=self.timeout,
|
|
|
|
capture_output=True,
|
|
|
|
text=True,
|
|
|
|
check=True,
|
|
|
|
)
|
|
|
|
except subprocess.CalledProcessError as e:
|
2022-11-18 22:43:41 -06:00
|
|
|
if e.stderr:
|
2022-10-15 04:19:04 -05:00
|
|
|
self.create_logger().error(e.stderr)
|
|
|
|
raise
|
|
|
|
|
2022-11-18 22:43:41 -06:00
|
|
|
current_app.logger.info('Passexec completed successfully')
|
2022-10-15 04:19:04 -05:00
|
|
|
self.last_result = now
|
|
|
|
self.password = p.stdout.strip()
|
|
|
|
return self.password
|
|
|
|
|
|
|
|
def is_expired(self):
|
|
|
|
if self.expiration_seconds is None:
|
|
|
|
return False
|
|
|
|
return self.last_result is not None and\
|
2023-11-03 06:25:24 -05:00
|
|
|
datetime.now(timezone.utc) - self.last_result \
|
2022-10-15 04:19:04 -05:00
|
|
|
>= timedelta(seconds=self.expiration_seconds)
|
|
|
|
|
|
|
|
def create_logger(self):
|
|
|
|
logger = logging.getLogger('passexec')
|
|
|
|
for h in current_app.logger.handlers:
|
|
|
|
logger.addHandler(h)
|
|
|
|
return logger
|