Keep the Execute action available to users holding only the EXECUTE privilege - #20415
Open
Mano515 wants to merge 1 commit into
Open
Keep the Execute action available to users holding only the EXECUTE privilege#20415Mano515 wants to merge 1 commit into
Mano515 wants to merge 1 commit into
Conversation
Reading a routine's source needs SHOW_ROUTINE, the global SELECT privilege or being its DEFINER, so SHOW CREATE returns a NULL body to a user who was only granted EXECUTE. getRow() treated that missing definition as a missing privilege and greyed out the Execute action, and getDataFromName() bailed out on it too, which would have broken the execution dialog. Decide the action from the privilege instead, and rebuild a parameters-only definition from INFORMATION_SCHEMA.PARAMETERS when the source is hidden. That rebuilt definition has no body, so it is opt-in and only the execution paths ask for it: the editor keeps refusing a routine it cannot read, or it would save the empty body back. Signed-off-by: Manuel <hmanuel515@hotmail.fr>
Author
|
The workflows here are waiting on approval, so I ran the same commit on my fork in the meantime:
All green. Mentioning it mainly because the patch bumps one counter in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #19543
Problem
After
GRANT EXECUTE ON db.* TO john,johncan runCALL Hello('world')from theMySQL CLI, but the Execute action for that routine is greyed out in phpMyAdmin.
Root cause
The privilege check itself is fine:
Util::currentUserHasPrivilege('EXECUTE', $db)returns
true, becauseINFORMATION_SCHEMA.SCHEMA_PRIVILEGESdoes report the grant.The action is disabled by a second, unrelated condition.
Routines::getRow()alsoneeds the routine's parameter list, which it obtains by parsing the routine's DDL:
and the template requires both flags:
{% if has_execute_privilege and execute_action is not empty %}getDefinition()runsSHOW CREATE PROCEDURE. Reading a routine's source requiresSHOW_ROUTINE, or the globalSELECTprivilege, or being the routine'sDEFINER;EXECUTEalone is not enough. The server does not reject the statement in that case —it returns a row whose
Create Procedurecolumn isNULL:getDefinition()maps that tonull,$executeActionstays empty, and the row rendersthe disabled
bd_nextpageicon. In short, phpMyAdmin was treating "may I read thisroutine's source?" as if it answered "may I run it?".
The same
$definition === nullbail-out exists ingetDataFromName(), so simplyenabling the link is not enough: the execution dialog would fail with
"No routine with name ... found in database ...".
Fix
getRow()decides the Execute action from the privilege, and only uses the DDL as anoptimisation. When the DDL is readable the existing behaviour is unchanged (routines
with no input parameters still get one-click
execute_routine). When it is not, itfalls back to
execute_dialog.getDataFromName()rebuilds a parameters-only definition fromINFORMATION_SCHEMA.PARAMETERSwhen the source is hidden, which the server doesexpose to a user holding
EXECUTE. It is fed to the existing parser, so length,UNSIGNED,ENUM/SETvalues and parameter direction are all derived by the samecode as before.
Because such a rebuilt definition carries no body, it is opt-in through a new
$paramsOnlyDataargument that only the two execution paths pass. The routine editorkeeps refusing a routine whose source it cannot read — otherwise saving it back would
recreate the routine with an empty body. There is a regression test for this.
The baseline files only move by three counters, for one extra
escapeString($db)calland one extra
(string)cast on a column read from INFORMATION_SCHEMA — both follow theglobal $db/ mixed-array patterns already used all over this class.Testing
Verified against MySQL 8.4.2 (the version in the report) and MariaDB 11.4.4, with
exactly the grants from the issue,
GRANT USAGE ON *.*+GRANT EXECUTE ON db.*. Bothservers behave the same:
SHOW CREATEreturns aNULLbody,ROUTINE_DEFINITIONisNULL,INFORMATION_SCHEMA.PARAMETERSis readable, andCALLworks — so the bug andthe fix are identical on both.
ic_bd_nextpage(disabled)ic_b_nextpage(enabled)CALL Hello('world')SET @p0='world'; CALL Hello(@p0);→Hello worldAddUp(10, 0.25)(FUNCTION)SELECT AddUp(@p0, @p1)→10.25Also checked that
IN/OUT/INOUT,int unsigned,enum('x','y')anddecimal(10,2)all parse identically whether they come from the real DDL or from therebuilt one, and that Edit and Export stay disabled for this user. Both servers report
PARAMETER_MODEfor every parameter past position 0, including a function's, so therebuilt parameter list never has a missing direction.
Three tests were added. The two that cover the reported bug fail without this change.
phpcs,phpstanandpsalmare clean on the changed files, and the test suite showsno new failures.
Not covered
A grant on one specific routine,
GRANT EXECUTE ON PROCEDURE db.Hello TO jane, stillshows a disabled Execute action.
Util::currentUserHasPrivilege()only looks atUSER_PRIVILEGESandSCHEMA_PRIVILEGES, and a routine-level grant appears in neither(it lives in
mysql.procs_priv, which the user cannot read) — the long-standing commentabove the check already says as much.
masterhas the same gap.Closing it would mean changing how the privilege is detected rather than when the
definition is needed, so it felt like a separate change. Happy to fold it in if you would
rather see both handled at once.