diff --git a/ChangeLog b/ChangeLog index a52649af89..a47ed75d2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -36,6 +36,7 @@ phpMyAdmin - ChangeLog - issue #12272 Adding a new row with default enum goes to no selection when you want to add more then 2 rows - issue #12487 Drag and drop import prevents file dropping to blob column file selector on the insert tab - issue #12554 Absence of scrolling makes it impossible to read longer text values in grid editing +- issue #12530 "Edit routine" crashes when the current user is not the definer, even if privileges are adequate 4.6.4 (2016-08-16) - issue [security] Weaknesses with cookie encryption, see PMASA-2016-29 diff --git a/js/messages.php b/js/messages.php index 9b8ecf2b33..c79629dd1c 100644 --- a/js/messages.php +++ b/js/messages.php @@ -385,6 +385,8 @@ $js_messages['strDeleting'] = __('Deleting'); $js_messages['MissingReturn'] = __('The definition of a stored function must contain a RETURN statement!'); $js_messages['strExport'] = __('Export'); +$js_messages['NoExportable'] + = __('No routine is exportable. Required privileges may be lacking.'); /* For ENUM/SET editor*/ $js_messages['enum_editor'] = __('ENUM/SET editor'); diff --git a/js/rte.js b/js/rte.js index ca2f6d4d6c..9a189aeae3 100644 --- a/js/rte.js +++ b/js/rte.js @@ -128,6 +128,11 @@ RTE.COMMON = { var count = export_anchors.length; var returnCount = 0; + // No routine is exportable (due to privilege issues) + if (count === 0) { + PMA_ajaxShowMessage(PMA_messages.NoExportable); + } + export_anchors.each(function () { $.get($(this).attr('href'), {'ajax_request': true}, function (data) { returnCount++; @@ -149,6 +154,7 @@ RTE.COMMON = { } else { $.get($this.attr('href'), {'ajax_request': true}, showExport); } + PMA_ajaxRemoveMessage($msg); function showExport(data) { if (data.success === true) { diff --git a/libraries/DatabaseInterface.php b/libraries/DatabaseInterface.php index db86343fb3..72fd3e42c7 100644 --- a/libraries/DatabaseInterface.php +++ b/libraries/DatabaseInterface.php @@ -1824,10 +1824,11 @@ class DatabaseInterface * @param string $db db name * @param string $which PROCEDURE | FUNCTION | EVENT | VIEW * @param string $name the procedure|function|event|view name + * @param object $link MySQL link * * @return string the definition */ - public function getDefinition($db, $which, $name) + public function getDefinition($db, $which, $name, $link = null) { $returned_field = array( 'PROCEDURE' => 'Create Procedure', @@ -1838,7 +1839,7 @@ class DatabaseInterface $query = 'SHOW CREATE ' . $which . ' ' . Util::backquote($db) . '.' . Util::backquote($name); - return($this->fetchValue($query, 0, $returned_field[$which])); + return($this->fetchValue($query, 0, $returned_field[$which], $link)); } /** diff --git a/libraries/rte/rte_export.lib.php b/libraries/rte/rte_export.lib.php index 55f9c2da57..94b59ff5aa 100644 --- a/libraries/rte/rte_export.lib.php +++ b/libraries/rte/rte_export.lib.php @@ -44,15 +44,16 @@ function PMA_RTE_handleExport($export_data) } else { $_db = htmlspecialchars(PMA\libraries\Util::backquote($db)); $message = __('Error in processing request:') . ' ' - . sprintf(PMA_RTE_getWord('not_found'), $item_name, $_db); - $response = Message::error($message); + . sprintf(PMA_RTE_getWord('no_view'), $item_name, $_db); + $message = Message::error($message); + if ($GLOBALS['is_ajax_request'] == true) { $response = PMA\libraries\Response::getInstance(); $response->setRequestStatus(false); $response->addJSON('message', $message); exit; } else { - $response->display(); + $message->display(); } } } // end PMA_RTE_handleExport() @@ -70,6 +71,9 @@ function PMA_EVN_handleExport() if (! empty($_GET['export_item']) && ! empty($_GET['item_name'])) { $item_name = $_GET['item_name']; $export_data = $GLOBALS['dbi']->getDefinition($db, 'EVENT', $item_name); + if (! $export_data) { + $export_data = false; + } PMA_RTE_handleExport($export_data); } } // end PMA_EVN_handleExport() @@ -89,13 +93,20 @@ function PMA_RTN_handleExport() && ! empty($_GET['item_type']) ) { if ($_GET['item_type'] == 'FUNCTION' || $_GET['item_type'] == 'PROCEDURE') { - $export_data = "DELIMITER $$\n" - . $GLOBALS['dbi']->getDefinition( + $rtn_definition + = $GLOBALS['dbi']->getDefinition( $db, $_GET['item_type'], $_GET['item_name'] - ) - . "$$\nDELIMITER ;\n"; + ); + if (! $rtn_definition) { + $export_data = false; + } else { + $export_data = "DELIMITER $$\n" + . $rtn_definition + . "$$\nDELIMITER ;\n"; + } + PMA_RTE_handleExport($export_data); } } diff --git a/libraries/rte/rte_list.lib.php b/libraries/rte/rte_list.lib.php index 7d08d984b7..85d27d6b54 100644 --- a/libraries/rte/rte_list.lib.php +++ b/libraries/rte/rte_list.lib.php @@ -196,9 +196,24 @@ function PMA_RTN_getRowForList($routine, $rowclass = '') $retval .= " \n"; $retval .= " \n"; $retval .= " \n"; + + // this is for our purpose to decide whether to + // show the edit link or not, so we need the DEFINER for the routine + $where = "ROUTINE_SCHEMA " . PMA\libraries\Util::getCollateForIS() . "=" + . "'" . PMA\libraries\Util::sqlAddSlashes($db) . "' " + . "AND SPECIFIC_NAME='" . PMA\libraries\Util::sqlAddSlashes($routine['name']) . "'" + . "AND ROUTINE_TYPE='" . PMA\libraries\Util::sqlAddSlashes($routine['type']) . "'"; + $query = "SELECT `DEFINER` FROM INFORMATION_SCHEMA.ROUTINES WHERE $where;"; + $routine_definer = $GLOBALS['dbi']->fetchValue($query, 0, 0, $GLOBALS['controllink']); + + $curr_user = $GLOBALS['dbi']->getCurrentUser(); + // Since editing a procedure involved dropping and recreating, check also for // CREATE ROUTINE privilege to avoid lost procedures. - if (PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db)) { + if ((PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db) + && $curr_user == $routine_definer) + || $GLOBALS['is_superuser'] + ) { $retval .= ' \n"; $retval .= " \n"; - $retval .= ' ' . $titles['Export'] . "\n"; + if ((PMA\libraries\Util::currentUserHasPrivilege('CREATE ROUTINE', $db) + && $curr_user == $routine_definer) + || $GLOBALS['is_superuser'] + ) { + $retval .= ' ' . $titles['Export'] . "\n"; + } else { + $retval .= " {$titles['NoExport']}\n"; + } $retval .= " \n"; $retval .= " \n"; $retval .= ' fetchSingleRow($query); + $routine = $GLOBALS['dbi']->fetchSingleRow($query, 'ASSOC', $link); if (! $routine) { return false; @@ -595,13 +603,19 @@ function PMA_RTN_getDataFromName($name, $type, $all = true) $retval['item_name'] = $routine['SPECIFIC_NAME']; $retval['item_type'] = $routine['ROUTINE_TYPE']; - $parser = new SqlParser\Parser( - $GLOBALS['dbi']->getDefinition( + $definition + = $GLOBALS['dbi']->getDefinition( $db, $routine['ROUTINE_TYPE'], - $routine['SPECIFIC_NAME'] - ) - ); + $routine['SPECIFIC_NAME'], + $link + ); + + if ($definition == NULL) { + return false; + } + + $parser = new SqlParser\Parser($definition); /** * @var CreateStatement $stmt @@ -1283,7 +1297,7 @@ function PMA_RTN_handleExecute() if (! empty($_REQUEST['execute_routine']) && ! empty($_REQUEST['item_name'])) { // Build the queries $routine = PMA_RTN_getDataFromName( - $_REQUEST['item_name'], $_REQUEST['item_type'], false + $_REQUEST['item_name'], $_REQUEST['item_type'], false, true ); if ($routine === false) { $message = __('Error in processing request:') . ' '; @@ -1481,7 +1495,7 @@ function PMA_RTN_handleExecute() * Display the execute form for a routine. */ $routine = PMA_RTN_getDataFromName( - $_GET['item_name'], $_GET['item_type'], true + $_GET['item_name'], $_GET['item_type'], true, true ); if ($routine !== false) { $form = PMA_RTN_getExecuteForm($routine); diff --git a/libraries/rte/rte_words.lib.php b/libraries/rte/rte_words.lib.php index 18d757d252..3205c2e7fb 100644 --- a/libraries/rte/rte_words.lib.php +++ b/libraries/rte/rte_words.lib.php @@ -31,6 +31,14 @@ function PMA_RTE_getWord($index) 'no_create' => __( 'You do not have the necessary privileges to create a routine' ), + 'no_edit' => __( + 'No routine with name %1$s found in database %2$s. ' + . 'You might be lacking the necessary privileges to edit this routine' + ), + 'no_view' => __( + 'No routine with name %1$s found in database %2$s. ' + . 'You might be lacking the necessary privileges to view/export this routine' + ), 'not_found' => __('No routine with name %1$s found in database %2$s'), 'nothing' => __('There are no routines to display.'), 'title' => __('Routines'),