Skip to content

Commit 0c6740f

Browse files
ardentperfdpage
authored andcommitted
feat: include authenticated user identity in HTTP access log (pgadmin-org#9991)
Set an X-Remote-User response header containing the authenticated username on every request when the LOG_AUTHENTICATED_USER config option is enabled (disabled by default). This allows the HTTP access log to be configured to include user identity via standard log format directives (%({x-remote-user}o)s in gunicorn, %{X-Remote-User}o in Apache) without requiring any changes to pgAdmin's session or authentication behaviour. The default gunicorn access log format is updated to surface the header. The username is sanitised to a header-safe value: it is transliterated to Latin-1 (HTTP headers are Latin-1 only) and any non-printable characters, including CR/LF, are stripped, so unusual usernames cannot cause a 500 on every response.
1 parent 888a053 commit 0c6740f

3 files changed

Lines changed: 25 additions & 0 deletions

File tree

pkg/docker/gunicorn_config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55

66
gunicorn.SERVER_SOFTWARE = "Python"
77

8+
# Include the authenticated user identity in the access log.
9+
# %({x-remote-user}o)s reads the X-Remote-User response header set by pgAdmin
10+
# for authenticated requests; unauthenticated requests log '-'.
11+
access_log_format = (
12+
'%(h)s %(l)s %({x-remote-user}o)s %(t)s "%(r)s" %(s)s %(b)s '
13+
'"%(f)s" "%(a)s"'
14+
)
15+
816
if JSON_LOGGER:
917
logconfig_dict = {
1018
"version": 1,

web/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@
301301
LOG_ROTATION_SIZE = 10 # In MBs
302302
LOG_ROTATION_AGE = 1440 # In minutes
303303
LOG_ROTATION_MAX_LOG_FILES = 90 # Maximum number of backups to retain
304+
# Include the authenticated username in the X-Remote-User response header so
305+
# it can be captured in the HTTP access log. Disabled by default.
306+
LOG_AUTHENTICATED_USER = False
304307
##########################################################################
305308
# Server Connection Driver Settings
306309
##########################################################################

web/pgadmin/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,20 @@ def before_request():
873873

874874
@app.after_request
875875
def after_request(response):
876+
if config.LOG_AUTHENTICATED_USER:
877+
if current_user.is_authenticated and current_user.username:
878+
# HTTP headers are latin-1 only, so transliterate anything
879+
# outside that range to avoid gunicorn 500s for unicode names.
880+
safe = current_user.username.encode(
881+
'latin-1', 'replace').decode('latin-1')
882+
# CR/LF and other control chars are valid latin-1 but Werkzeug
883+
# rejects them in header values (would 500 every request for
884+
# that user), so drop any non-printable characters too.
885+
safe = ''.join(c for c in safe if c.isprintable())
886+
response.headers['X-Remote-User'] = safe
887+
else:
888+
response.headers.pop('X-Remote-User', None)
889+
876890
if 'key' in request.args:
877891
domain = dict()
878892
if config.COOKIE_DEFAULT_DOMAIN and \

0 commit comments

Comments
 (0)