Adds browser support for PostgreSQL 14+ extended statistics objects
(CREATE STATISTICS), with the create/edit dialog, reverse engineered SQL,
the computed values ANALYZE collected where the role may see them, and
schema diff support. Completes Murtuza Zabuawala's #9748.
Co-authored-by: Dave Page <dpage@pgadmin.org>
Adds OAUTH2_SERVER_GROUP_CLAIM and OAUTH2_SERVER_GROUP_CLAIM_MAPPING. When
the claim is configured for a provider, shared servers owned by other users
are only visible and accessible if their server group's name appears in the
(optionally mapped) claim values; if the claim is missing, none are. The
user's own servers, and users of other authentication methods, are
unaffected. The claim is resolved at login and only stored once the login
has been authorised.
Co-authored-by: Dave Page <dpage@pgadmin.org>
When SPNEGO negotiation failed, the exception object was passed to flash(), so the session could not be serialised and the login page was replaced by a 500 JSON response. A generic message is now flashed; the detail is already logged.
Also fix a redirect to a non-existent 'authenticate.kerberos_login' endpoint (it is 'kerberos.login'), which had caused every Kerberos mock test to skip unconditionally, and return the username from Kerberos auto-creation so a user's first login is not rejected with "Incorrect username or password".
Co-authored-by: Dave Page <dpage@pgadmin.org>
Share one explicit react-dnd manager between DataGridView and PgTreeView. react-arborist bundles react-dnd 14, which stores its manager under the same global key as react-dnd 16 but keeps a separate reference count, so unmounting one provider could drop the manager while its HTML5 backend was still attached to window.
Moves table construction to useTable() with a shared tableFeatures() registry (filtering, sorting, selection, expanding, sizing/resizing, visibility, row models, sort and filter functions), renames sortingFn to sortFn, adapts to v9 state access, prototype-bound row/cell methods and getRowCanExpand, feeds the DataGridView search state through table state instead of setOptions(), and lets Jest transform the ESM-only @tanstack packages.
Also points the react-checkbox-tree webpack alias at lib/index.js, the 2.1 ESM build, as lib/index.esm.js is a stale 2.0.2 build that leaves the app blank.
* Parse react-frame-component as CommonJS so 5.3 bundles correctly
react-frame-component 5.3 added "type": "module" to its package.json
whilst mapping the "require" condition of its exports to a UMD build.
Babel compiles our imports to require(), so webpack resolves the UMD
file and, honouring the package type, parses it as an ES module. With no
exports object in scope, the UMD wrapper falls through to its browser
global branch and the module exports nothing, so <Frame> is undefined.
Opening the Query Tool then throws React error #130 and unmounts the
whole app, which is why every feature test timed out with the 5.3.2
bump in #10460. Node shows the same packaging fault: require() of the
package returns an empty object.
Tell webpack to parse the package as javascript/auto so the UMD takes
its CommonJS branch. Jest is unaffected, as it loads the UMD as CommonJS.
* Bump react-frame-component to 5.3.2.
Take the version the webpack rule exists for in the same change, so CI
exercises the rule against it.
YAML consumes the quotes around a Compose scalar, so a value such as
PGADMIN_CONFIG_MAIL_SERVER: "mail.example.com" reaches config_distro.py
as a bare name and fails with NameError at startup. Document the nested
quoting needed in map syntax and env_file, how the list form differs,
escaping $ as $$, when config_distro.py is regenerated, and add a Compose
example.
Adds a pgTimeTable Chains node under Servers when pg_timetable is
installed in the maintenance database, with Tasks beneath each chain.
Chains and tasks can be created, edited and deleted, a chain can be
triggered with Run Now, and both have statistics tabs. Includes
regression tests, CI installation of pg_timetable, and documentation.
Reformat pkg/docker/entrypoint.sh to use bash conditionals, arithmetic and
parameter expansion instead of the workarounds needed for BusyBox ash.
The email validation settings and PGADMIN_DEFAULT_EMAIL are now read from
the environment inside Python rather than interpolated into the Python
source, so an address containing an apostrophe works, and a Python error
during validation stops the container instead of passing silently.
Correct 162 entries in the zh_Hans_CN catalogue: strings that are wrong
rather than merely worded differently, plus terminology aligned with
the Chinese PostgreSQL documentation.
- Restore the order of positional %s placeholders in three strings.
The JavaScript gettext fills %s strictly left to right, so the
reordered translations garbled the update notice, the preference
load error and the user password length error.
- Keep the half-width colons in ': error: ', which ProcessDetails.jsx
matches with a regex to highlight error lines, and the SQL comment
marker in '-- missing definition'.
- Fix strings whose meaning was wrong or reversed, e.g. the ERD drag
and drop message, relhassubclass, RESTART IDENTITY, ENABLE REPLICA,
COLLATABLE, CREATE TABLE ... LIKE, and the DBMS Scheduler
"Schedule" object being called a task.
- Give distinct labels to adjacent fields that shared one, and remove
leftovers such as "表(s)", "会话token" and an untranslated
"date/time".
- Use the established Chinese terms where the translation picked the
wrong sense, e.g. Dependents (previously given the word for
dependencies), local variables and the package body.
- Follow the terminology of the Chinese PostgreSQL documentation:
注释 (comment), 字符分类 (LC_CTYPE), 操作符 (operator), 聚簇
(CLUSTER), 提供者 (provider), 流复制 (streaming replication),
预备事务 (prepared transaction), 载荷 (NOTIFY payload) and 过滤
(filter).
- Translate Tab as 标签页 throughout, and the month-day options
1st-31st as 1 日-31 日.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
When an index metadata query failed, the Properties and SQL handlers
could replace the original database error with 'string indices must be
integers', or with an error about using a Response as index metadata.
get_column_details() read rset['rows'] before checking the query status,
and get_column_details(), get_include_details() and get_sql() returned
HTTP responses where their callers expect data.
Raise ExecuteError instead, as get_reverse_engineered_sql() already
does, so the original database error reaches the user. Adds regression
tests covering Properties, SQL, modified SQL and update.
The dialog hardcoded the pgpass flag to false, so it always demanded the
current password even when the connection authenticates via a passfile.
Pass the flag from check_pgpass through to the schema instead.
check_pgpass and change_password also disagreed about when a passfile is
in use: the former treated a password entered at connect but not saved
as a passfile connection, which would now disable the Current Password
field whilst the endpoint still required it. Both now use a shared
is_using_passfile() helper.
Fixes#10449
The Windows, Linux and macOS builds now read the Electron version from
runtime/yarn.lock using 'yarn info electron --json' inside runtime/,
rather than querying the npm registry or using the lower bound of the
range in runtime/package.json. This needs no network access or
node_modules, and packages exactly the version that yarn install
resolves. The build stops with an error if the lockfile is missing or
does not yield a valid version.
A fork with Actions enabled inherits the nightly schedule but none of the
signing secrets or self-hosted runners, so its snapshot fails at the macOS
certificate import and leaves the Windows job queued for a day. The
location job now skips a scheduled run anywhere else, and every build job
waits on it so that the whole run skips with it. A manual dispatch still
runs anywhere.
The test asserted that no X-Remote-User header is sent on the request
after a logout, which only holds in server mode. In desktop mode the
before_request hook logs the desktop user straight back in, so the
header is reported again, and every run-python-tests-pg job (which runs
in desktop mode) has failed on it since #10410. Assert the mode-specific
behaviour instead.
The one-step "signtool sign /fd sha256" fails on the build host with
0xc0000225 before the PIN is asked for, because crypt32 converts the
legacy CryptoAPI key handle to a CNG one for SHA-2 digests, and no CNG
provider is registered under the name of Certum's crypto3 CSP. Rebinding
to cryptoCertum3 KSP is not an option, as it cannot see the Common
profile's code-signing key.
signtool's split flow avoids the conversion, so pkg/win32/sign-files.bat
now does the signing in /dg, /ds, /di, timestamp and verify steps, with
one PIN prompt per call as before. Make.bat's :SIGN_FILES and Inno
Setup's SignTool both go through it, the latter via cmd /c.
Also default PGADMIN_SIGNTOOL_DIR to SDK 10.0.26100.0, the one installed
on the host, and match the PIN dialog on "Logowanie do profilu zwyk" so
that a Secure profile prompt is never answered with the Common PIN.
SIGNING.md gains the full setup procedure, the root cause and the split
signing flow.
A new Debian, Ubuntu, Fedora or EL release is named in seven places across
the build matrices, the snapshot and release workflows, the acceptance tests
and the publishing wrapper, and missing the wrapper leaves the packages built
and uploaded but never indexed. pkg/linux/PLATFORMS.md lists them all, with
the steps for adding a platform, retiring one and the server-side follow-up.
The platform lists in the Debian and Red Hat READMEs had drifted (Ubuntu
25.04, Fedora 40 and 41, RHEL 8), so they now point at the build matrices
instead.
#10355 moved signtool to /fd sha256 on the assumption that the key
provider repair described in pkg/win32/SIGNING.md made SHA-256 possible.
It does not: that repair binds the certificate to the Certum card's
legacy CryptoAPI provider, crypto3 CSP, which fails a SHA-256 file digest
in SignerSign() with 0xc0000225 before the PIN is requested, whilst
/fd sha1 signs and verifies. SHA-1 is what every previous release has
shipped with, so go back to it for both the component and the installer
signatures, and document why.
The timestamp digest stays at SHA-256, which works. Rebinding the
certificate to Certum's CNG provider is the likely route to SHA-256 file
digests, but is untested and is left for later.
The messages that report a missing prerequisite were changed from !VAR!
to %VAR% in commit afa0c773a, since the file does not enable delayed
expansion and they printed the variable name rather than the path. That
made them correct and unparseable at the same time: cmd expands %VAR%
when it parses a parenthesised block rather than when it runs it, and
PGADMIN_INNOTOOL_DIR defaults to "C:\Program Files (x86)\Inno Setup 6",
whose closing parenthesis then ended the IF block early. Every Windows
build has failed at "Checking the environment..." since, with "\Inno was
unexpected at this time", whether or not Inno Setup was installed, which
took the nightly snapshot with it.
The four checks move into a :REQUIRE_DIR subroutine that takes the
directory and the hint, and echoes both outside any block, where a
parenthesis is just a character. Enabling delayed expansion would also
have worked, but only by making ! a metacharacter for the whole file.
The other uses of these two paths are all inside double quotes, which
the parser does respect, so they were never affected.
The 'Change Password...' option on a server node was ambiguous, because it
changes the password of the PostgreSQL role that pgAdmin connects with, yet
read as though it might change the pgAdmin login password instead. The menu
option and its dialog are now called 'Change Server Password', with the
connected role name in the dialog title, and the equivalent option in the
user menu becomes 'Change pgAdmin Password', so that the two can no longer
be confused with one another.
The dialog's own 'User' field was always blank, since the userName prop that
showChangeServerPassword passes was never accepted by ChangePasswordContent;
it is now wired through, which also makes the existing documentation of that
field accurate. The documentation for both dialogs has been updated to match
the new names, along with a couple of button names that had drifted.
Fixes#9230.
Typing in the editor of a View/Edit Data tab promotes it to a Query Tool, but
that promotion only ever updated the component's own state: the tab's saved
metadata kept the original tool URL, with is_query_tool=false in its query
string. On the next start-up the tab was therefore restored as a View/Edit Data
tab, and View/Edit Data tabs execute their query as soon as the connection is
made, so whatever SQL the user had left in the editor ran unprompted. A
restored DELETE or UPDATE running by itself is not a pleasant way to start the
day.
toggleQueryTool now rewrites is_query_tool in the panel's stored toolUrl and
saves the layout, so a promoted tab comes back as a Query Tool and no longer
takes the auto-execute path. The URL is kept relative, because in Desktop mode
the runtime picks a fresh port on every launch and an absolute URL would point
the restored tab at a dead origin.
is_query_tool and restore are also normalised to real booleans up front rather
than compared against the string 'true' in each place they are read, and a
failed restore no longer leaves the editor disabled with nothing logged.
Fixes#10031
Adds Caddy examples alongside the existing NGINX ones for hosting pgAdmin with
Gunicorn or uWSGI, both at the root of a server and under a sub-directory,
mirroring the structure of the NGINX sections. Caddy does not speak the native
uwsgi protocol, so its uWSGI examples run uWSGI in HTTP mode instead.
Documents the two things that must line up for a sub-directory deployment
behind any reverse proxy: the proxy forwarding the request path unmodified
with an X-Script-Name header, and a SCRIPT_NAME environment variable on the
pgAdmin process itself, which is read at startup to set APPLICATION_ROOT and
SESSION_COOKIE_PATH. The missing environment variable is what caused the
"login succeeds then bounces back to the login page" report in #10334.
Also notes that pgAdmin trusts X-Script-Name and X-Scheme unconditionally,
unlike the X-Forwarded-* headers, so every example now either overwrites or
clears both rather than passing on whatever the client sent; warns that the
systemd unit shipped with Caddy sets PrivateTmp=true, which hides a socket
created in the system /tmp; and refreshes the stale python3.7 paths.
The PostgreSQL and Kerberos utilities and libraries we ship in the
Windows package are not signed, whilst the installer, the uninstaller
and pgAdmin4.exe are. Nothing verifies an Authenticode signature when
these run, so this is not a security fix, but it does make life easier
for organisations running application allow-listing under WDAC or
AppLocker, where publisher rules are simpler and more durable than path
or hash based ones, and it cuts down on antivirus false positives.
The signtool invocation moves into a :SIGN_FILES subroutine, and a new
:SIGN_COMPONENTS step signs everything we build ourselves once
:CREATE_RUNTIME_ENV has staged it. Names are matched through DIR /B so
the optional libraries are skipped when absent, and the whole thing is a
no-op when PGADMIN_WINDOWS_CSC is unset, so unsigned developer builds
are unaffected. The Electron, Python and VC++ runtime components are
deliberately left alone, being third party binaries whose own signatures
ours would replace. Everything goes through a single signtool call,
since the hardware token prompts for a PIN on the first signature of a
session.
The file digest moves from SHA-1 to SHA-256 for both the runtime and the
installer. SHA-1 was never a deliberate choice: it dates from the move
to the Certum token, at a point where the certificate in the machine
store had no key provider link, which limits signing to SHA-1. The
buildfarm now repairs that link with certutil when it provisions the
host.
The four optional PostgreSQL libraries were copied without checking the
result; they now abort the build like the others do.
PGADMIN_POSTGRES_DIR and PGADMIN_KRB5_DIR defaulted to an EDB PostgreSQL
installation and an MIT Kerberos for Windows installation respectively,
whereas both sets of binaries have come from winpgbuild for some time
and the buildfarm points at C:\build64\postgresql and C:\build64\krb5.
The four "does not exist" messages that report a missing prerequisite
used !VAR!, but the SETLOCAL at the top of the file does not enable
delayed expansion, so they printed the variable name rather than the
path that was looked for.
Finally, the acceptance test now verifies the signatures on everything
:SIGN_COMPONENTS signs, walking the same list of names where the files
land: none of them exist outside the installer until the installer has
run, so that is the only place a published build can be checked. The
expected publisher is taken from the installer itself rather than hard
coded, so a component signed by somebody else is caught without the
workflow needing an edit when the certificate is renewed.
In the four GitHub Actions workflows the flag is documentation only, since
Yarn's enableImmutableInstalls already defaults to true whenever CI is set in
the environment, but making it explicit means the intent is visible in the
workflow rather than implied by the runner.
The six installs that run outside CI are where this actually changes
behaviour: the Makefile, the Dockerfile, and the pip, Linux, macOS and Windows
packaging builds all run with yarn.lock beside them, and until now any of them
would quietly rewrite the lockfile rather than fail. A release build that
rewrites the lockfile is shipping something other than what we reviewed, so
failing loudly there is the point of the exercise.
The lock was written against the rolling tags because the per-version
releases did not exist when it was generated, and the refresh tool assumed
PostgreSQL was tagged by major, which it is not: the release that never
changes is postgresql-18.6, whilst only the rolling one says 18. The tool
now resolves a major to the version pgbuild's manifest names, and a tag that
does not exist is an error rather than something to fall back from, since a
lock recording an archive nobody can fetch is worse than no lock at all.
A lock change decides which PostgreSQL, OpenSSL and Kerberos binaries end up
inside the installer, so it is exactly the kind of change those checks exist
for, and without this it was the one packaging input that could move without
either of them running.
The install-pgbuild-deps action fetched PostgreSQL, OpenSSL, Kerberos, zstd
and lz4 from pgbuild release tags ending in -latest, whose assets are
replaced in place on every rebuild, and unpacked them with no checksum or
signature check anywhere in between. Since the resulting build is
Authenticode signed on Windows and Developer ID signed and notarised on
macOS, that made write access to pgbuild equivalent to write access to what
pgAdmin ships, and it meant a build was not reproducible either, the same
commit producing different binaries depending on the day it was built. This
is pgadmin-org/pgbuild issue 1.
pkg/pgbuild-deps.lock now records, for every dependency the action can
fetch on either macOS architecture and on Windows, the per-version release
tag to download from and the SHA-256 its archive must hash to. The action
fetches that tag, checks the digest before unpacking, and fails on a
mismatch or on a dependency the lock does not mention, since an unpinned
dependency reaching a signing build is the thing this is meant to prevent.
pgbuild publishes a .sha256 beside each archive, which is deliberately not
consulted: whoever rebuilds an archive rebuilds that too, so the lock is
the only thing here that pins anything.
Being out of date is a different matter from being wrong, so once an
archive is verified the action asks pgbuild what it has published since,
and warns rather than fails when the pinned build is no longer the newest.
That check is best effort and never fails a build whose downloads have
already been verified.
tools/refresh_pgbuild_lock.py rewrites the lock, taking the version to pin
from pgbuild's own manifest.json and working one package at a time so that
bumping OpenSSL does not restate every other digest. The diff it produces,
with the upstream version visible in the tag column, is the audit trail the
previous arrangement had none of.
The digests were taken from the archives pgbuild publishes today, which are
the same archives its per-version releases will carry, but those releases
do not exist yet, so the tags in the lock cannot be fetched until pgbuild's
own change lands.
The deployed files are generated rather than hand written, and that option is
not one we can set there, so the templates described a restriction production
does not have. What a key may do is still bounded by restrict and the forced
command, which is the part that matters; what is lost is the second answer to
where it may be used from.
It has been failing check-python-style since it landed: five lines over the
limit, and two breaks before a binary operator, which count here because
.pycodestyle sets an explicit ignore list and so loses pycodestyle's own
default exemption for W503.
The web server unpacks this into the documentation root and then hands
docloader the directory name pgadmin4-<version>-docs, so a flat archive would
have scattered several hundred HTML files across that root and then failed on
a directory that was never created. Every published tarball carries that top
level; the one assembled here did not, and the first release to notice would
have been the next one.
sync-ftp-to-s3.py and purge_pgadmin_releases.sh have been living in the
pgaweb checkout under tools/, which was never where they belonged: neither
touches Django, the site database or Varnish, and both work on the download
tree at /var/ftp/pgadmin4, so they are part of publishing rather than part of
the website. They move here, renamed to match the hyphenated names of the
scripts already in this directory, and the README gains them in the file
table, in the install line and in the prose that explains what lives where.
The sync-s3 verb now runs sync-ftp-to-s3.py out of PUBLISH_DIR instead of
TOOLS_DIR, which makes this a deployment-ordering change: the wrapper and the
copy of the script it names must both be installed on the download server
before the pgaweb side deletes its own copy, and the comment on the verb says
so. The three verbs that remain genuinely website operations, create-release,
load-docs and purge-cache, still reach into TOOLS_DIR and are untouched.
purge-pgadmin-releases.sh deliberately gets no verb. It is a by-hand
operation that deletes published content, it defaults to a dry run that
somebody is expected to read before re-running with --live, and the rebuild
scripts it pairs with are already documented as standalone; handing a
compromised runner the ability to delete releases would undo most of what the
wrapper is for.
Both scripts are otherwise unchanged apart from the filename in the purge
script's own header comment, and four lines in the Python rewrapped to 79
columns because pkg/ is covered by check-pep8 and the originals were not.
The runner has carried both labels all along, so this is the last of the
machine names out of the tree; roadie can stay on the runner until these have
run green, and then go.