Files
pgadmin4/web/pgadmin/utils/constants.py
T
a0c8f4857f Restrict connection services to an administrator's allowlist in server mode. #10654
libpq looks a service up in the connection service file of the account
pgAdmin runs as, and an entry there can hold a password or name a
password file or client certificate. In server mode any user could set a
server's service, or a 'service' connection parameter, to one of those
entries and connect with its credentials. Likewise, a PGPASSWORD or
PGSERVICE variable set for that account was used by libpq for every
connection that had no value of its own, and passed on to the utilities.

In server mode a service may now only be used if it is listed in the new
SERVER_MODE_ALLOWED_SERVICES setting, which is empty by default. Creating
or updating a server, an ad hoc workspace connection or a servers.json
import that names any other service is refused, and connecting to an
existing server, or running psql or a utility for it, fails with a
message that names the setting. The 'servicefile' connection parameter is
refused in server mode, and PGPASSWORD and PGSERVICE are removed from
pgAdmin's environment at startup, with a warning. PGSERVICEFILE and
PGSYSCONFDIR are kept, since they locate the listed services. Desktop
mode is unchanged.

The allowlist is checked again on the final connection string, as libpq
will parse it, so a service smuggled in through a crafted parameter key
is refused however the parameters were serialised. pg_dumpall expands a
--database value containing "=" as a connection string, so the
maintenance database name of Backup Server and Globals is wrapped in one
and a name such as "service=name" no longer selects a service from the
service account's service file. client_files_isolated() treats a missing
PGADMIN_RUNTIME as "not the desktop runtime", so "setup.py load-servers"
no longer fails on any servers.json containing a Service and still
enforces the allowlist in server mode.

The tests run the real job-creation path of every tool that starts a
utility, and check that a service that is not listed is refused before
the utility starts, a listed one is exported as PGSERVICE, and a service
or servicefile connection parameter, or a database name that looks like
a connection string, never selects a service.

Compatibility: in server mode, servers that use a service stop
connecting until the administrator lists it in
SERVER_MODE_ALLOWED_SERVICES, and a PGPASSWORD or PGSERVICE set in the
environment pgAdmin starts with is ignored. "setup.py load-servers" now
refuses a servers.json whose Service is not in
SERVER_MODE_ALLOWED_SERVICES (exit status 1) instead of printing an
error and exiting 0.

Co-authored-by: Dave Page <dpage@pgadmin.org>
Co-authored-by: hiteshjambhale <hitesh.jambhale@enterprisedb.com>
2026-10-24 19:36:12 +05:30

245 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
"""Application wide constants."""
from flask_babel import gettext
# Mimetypes
MIMETYPE_APP_HTML = 'text/html'
MIMETYPE_APP_JS = 'application/javascript'
MIMETYPE_APP_JSON = 'application/json'
NO_CACHE_CONTROL = 'no-cache, no-store, must-revalidate'
# Preference labels
PREF_LABEL_KEYBOARD_SHORTCUTS = gettext('Keyboard shortcuts')
PREF_LABEL_DISPLAY = gettext('Display')
PREF_LABEL_BREADCRUMBS = gettext('Object Breadcrumbs')
PREF_LABEL_OPTIONS = gettext('Options')
PREF_LABEL_EXPLAIN = gettext('Explain')
PREF_LABEL_EDITOR = gettext('Editor')
PREF_LABEL_CSV_TXT = gettext('CSV/TXT Output')
PREF_LABEL_RESULTS_GRID = gettext('Results grid')
PREF_LABEL_SQL_FORMATTING = gettext('SQL formatting')
PREF_LABEL_TABS_SETTINGS = gettext('Tab settings')
PREF_LABEL_REFRESH_RATES = gettext('Refresh rates')
PREF_LABEL_GRAPH_VISUALISER = gettext('Graph Visualiser')
PREF_LABEL_GEOMETRY_VIEWER = gettext('Geometry Viewer')
PREF_LABEL_USER_INTERFACE = gettext('User Interface')
PREF_LABEL_FILE_DOWNLOADS = gettext('File Downloads')
PREF_LABEL_AI = gettext('AI')
PGADMIN_STRING_SEPARATOR = '_$PGADMIN$_'
PGADMIN_NODE = 'pgadmin.node.%s'
UNAUTH_REQ = "Unauthorized request."
SERVER_CONNECTION_CLOSED = gettext(
'Not connected to server or connection with the server has been closed.')
# Query tool placeholder
QT_DEFAULT_PLACEHOLDER = '%DATABASE%/%USERNAME%@%SERVER%'
VW_EDT_DEFAULT_PLACEHOLDER = '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%'
# Data Types
DATATYPE_TIME_WITH_TIMEZONE = 'time with time zone'
DATATYPE_TIME_WITHOUT_TIMEZONE = 'time without time zone'
DATATYPE_TIMESTAMP_WITH_TIMEZONE = 'timestamp with time zone'
DATATYPE_TIMESTAMP_WITHOUT_TIMEZONE = 'timestamp without time zone'
# Error Messages
ERROR_MSG_TRANS_ID_NOT_FOUND = gettext(
'Transaction ID not found in the session.')
ERROR_MSG_FAIL_TO_PROMOTE_QT = gettext('FAIL TO PROMOTE VIEW/EDIT DATA '
'TO QUERY TOOL')
# Role module constant
ERROR_FETCHING_ROLE_INFORMATION = gettext(
'Error fetching role information from the database server.')
ERROR_FETCHING_DATA = gettext('Unable to fetch data.')
ERROR_SERVER_ID_NOT_SPECIFIED = gettext('Server ID not specified.')
# Authentication Sources
INTERNAL = 'internal'
LDAP = 'ldap'
KERBEROS = 'kerberos'
OAUTH2 = 'oauth2'
WEBSERVER = 'webserver'
SUPPORTED_AUTH_SOURCES = [INTERNAL,
LDAP,
KERBEROS,
OAUTH2,
WEBSERVER]
BINARY_PATHS = {
"as_bin_paths": [
{"version": "130000", "next_major_version": "140000",
"serverType": gettext("EDB Advanced Server 13"), "binaryPath": None,
"isDefault": False},
{"version": "140000", "next_major_version": "150000",
"serverType": gettext("EDB Advanced Server 14"), "binaryPath": None,
"isDefault": False},
{"version": "150000", "next_major_version": "160000",
"serverType": gettext("EDB Advanced Server 15"), "binaryPath": None,
"isDefault": False},
{"version": "160000", "next_major_version": "170000",
"serverType": gettext("EDB Advanced Server 16"), "binaryPath": None,
"isDefault": False},
{"version": "170000", "next_major_version": "180000",
"serverType": gettext("EDB Advanced Server 17"), "binaryPath": None,
"isDefault": False},
{"version": "180000", "next_major_version": "190000",
"serverType": gettext("EDB Advanced Server 18"), "binaryPath": None,
"isDefault": False}
],
"pg_bin_paths": [
{"version": "130000", "next_major_version": "140000",
"serverType": gettext("PostgreSQL 13"), "binaryPath": None,
"isDefault": False},
{"version": "140000", "next_major_version": "150000",
"serverType": gettext("PostgreSQL 14"), "binaryPath": None,
"isDefault": False},
{"version": "150000", "next_major_version": "160000",
"serverType": gettext("PostgreSQL 15"), "binaryPath": None,
"isDefault": False},
{"version": "160000", "next_major_version": "170000",
"serverType": gettext("PostgreSQL 16"), "binaryPath": None,
"isDefault": False},
{"version": "170000", "next_major_version": "180000",
"serverType": gettext("PostgreSQL 17"), "binaryPath": None,
"isDefault": False},
{"version": "180000", "next_major_version": "190000",
"serverType": gettext("PostgreSQL 18"), "binaryPath": None,
"isDefault": False}
]
}
UTILITIES_ARRAY = ['pg_dumpall', 'pg_dump', 'pg_restore', 'psql']
BG_PROCESS_ERROR_MSGS = {
3221225781: gettext('Unable to find a dll needed by the utility. Ensure '
'.dll files needed by the utility are in the same '
'folder as your executable.')
}
ENTER_EMAIL_ADDRESS = "Email address: "
USER_NOT_FOUND = gettext("The specified user ID (%s) could not be found.")
DATABASE_LAST_SYSTEM_OID = 16383
# Drivers
PSYCOPG3 = 'psycopg3'
# Shared storage
MY_STORAGE = 'my_storage'
ACCESS_DENIED_MESSAGE = gettext(
"Access denied: You’re having limited access. You’re not allowed to "
"Rename, Delete or Create any files/folders")
KEY_RING_SERVICE_NAME = 'pgAdmin4'
KEY_RING_USER_NAME = 'pgadmin4-master-password'
KEY_RING_USERNAME_FORMAT = KEY_RING_SERVICE_NAME + '-{0}-{1}'
KEY_RING_TUNNEL_FORMAT = KEY_RING_SERVICE_NAME + '-tunnel-{0}-{1}'
KEY_RING_DESKTOP_USER = KEY_RING_SERVICE_NAME + '-desktop-user-{0}'
class MessageType:
SUCCESS = 'Success',
ERROR = 'Error',
INFO = 'Info',
CLOSE = 'Close',
WARNING = 'Warning'
DBMS_JOB_SCHEDULER_ID = 999999
# String Constants
IP_ADDRESS_STRING = '{}/{}'
TWO_PARAM_STRING = '{0}/{1}'
SERVER_NOT_FOUND = gettext("Could not find the specified server.")
SSL_MODES = ['prefer', 'require', 'verify-ca', 'verify-full']
DATA_TYPE_WITH_LENGTH = [1560, 'bit', 1561, 'bit[]',
1562, 'varbit', 'bit varying',
1563, 'varbit[]', 'bit varying[]',
1042, 'bpchar', 'character',
1043, 'varchar', 'character varying',
1014, 'bpchar[]', 'character[]',
1015, 'varchar[]', 'character varying[]',
'vector', 'vector[]', 'halfvec', 'halfvec[]',
'sparsevec', 'sparsevec[]']
RESTRICTION_TYPE_DATABASES = 'databases'
RESTRICTION_TYPE_SQL = 'sql'
RESTRICT_COMMAND = '\\restrict'
# Connection parameters whose value is a file selected through the file
# manager, and so must be resolved to a complete path before it is handed
# to libpq. 'sslrootcert' is handled separately, because the literal value
# 'system' asks libpq for the trusted CA store rather than naming a file.
CONNECTION_PARAM_FILE_PATHS = [
'passfile', 'sslcert', 'sslkey', 'sslcrl'
]
# Connection parameters naming a client credential file. When one is not
# given, libpq falls back to ~/.pgpass, ~/.postgresql/postgresql.crt or
# ~/.postgresql/postgresql.key in the home directory of the account it runs
# as, which in server mode is the pgAdmin service account, not the user.
CONNECTION_PARAM_CREDENTIAL_FILES = ['passfile', 'sslcert', 'sslkey']
# Connection parameters whose value is a directory selected through the
# file manager, resolved in the same way but checked as a directory.
CONNECTION_PARAM_DIR_PATHS = ['sslcrldir']
# Connection parameters that pgAdmin stores as a boolean but libpq expects
# as an integer.
CONNECTION_PARAM_BOOLEANS = ['sslcompression', 'sslsni', 'gssdelegation']
# Connection parameters that libpq also reads from the environment, mapped
# to the variable that carries them. Taken from PQconninfoOptions in
# src/interfaces/libpq/fe-connect.c; parameters that libpq offers no
# environment variable for (keepalives and friends, sslpassword,
# sslkeylogfile, fallback_application_name, replication) are absent, and
# cannot be passed to a utility this way. The service name is not passed
# from the connection parameters; the server's service is set separately
# as PGSERVICE.
CONNECTION_PARAM_ENV_VARS = {
'application_name': 'PGAPPNAME',
'channel_binding': 'PGCHANNELBINDING',
'client_encoding': 'PGCLIENTENCODING',
'connect_timeout': 'PGCONNECT_TIMEOUT',
'gssdelegation': 'PGGSSDELEGATION',
'gssencmode': 'PGGSSENCMODE',
'gsslib': 'PGGSSLIB',
'hostaddr': 'PGHOSTADDR',
'krbsrvname': 'PGKRBSRVNAME',
'load_balance_hosts': 'PGLOADBALANCEHOSTS',
'max_protocol_version': 'PGMAXPROTOCOLVERSION',
'min_protocol_version': 'PGMINPROTOCOLVERSION',
'options': 'PGOPTIONS',
'passfile': 'PGPASSFILE',
'require_auth': 'PGREQUIREAUTH',
'requirepeer': 'PGREQUIREPEER',
'ssl_max_protocol_version': 'PGSSLMAXPROTOCOLVERSION',
'ssl_min_protocol_version': 'PGSSLMINPROTOCOLVERSION',
'sslcert': 'PGSSLCERT',
'sslcertmode': 'PGSSLCERTMODE',
'sslcompression': 'PGSSLCOMPRESSION',
'sslcrl': 'PGSSLCRL',
'sslcrldir': 'PGSSLCRLDIR',
'sslkey': 'PGSSLKEY',
'sslmode': 'PGSSLMODE',
'sslnegotiation': 'PGSSLNEGOTIATION',
'sslrootcert': 'PGSSLROOTCERT',
'sslsni': 'PGSSLSNI',
'target_session_attrs': 'PGTARGETSESSIONATTRS',
}