Merge branch 'QA_5_1-security' into master-security
Signed-off-by: Maurício Meneghini Fauth <mauricio@fauth.dev>
This commit is contained in:
commit
0bee94b27e
@ -50,6 +50,7 @@
|
||||
"composer/ca-bundle": "^1.2",
|
||||
"google/recaptcha": "^1.1",
|
||||
"nikic/fast-route": "^1.3",
|
||||
"paragonie/sodium_compat": "^1.17",
|
||||
"phpmyadmin/motranslator": "^5.0",
|
||||
"phpmyadmin/shapefile": "^3.0.1",
|
||||
"phpmyadmin/sql-parser": "^5.5",
|
||||
@ -81,6 +82,7 @@
|
||||
"ext-zip": "For zip import and export",
|
||||
"ext-gd2": "For image transformations",
|
||||
"ext-mbstring": "For best performance",
|
||||
"ext-sodium": "Better encryption performance",
|
||||
"tecnickcom/tcpdf": "For PDF support",
|
||||
"pragmarx/google2fa-qrcode": "^2.1 - For 2FA authentication",
|
||||
"bacon/bacon-qr-code": "^2.0 - For 2FA authentication",
|
||||
@ -88,6 +90,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"bacon/bacon-qr-code": "^2.0",
|
||||
"code-lts/u2f-php-server": "^1.2",
|
||||
"php-webdriver/webdriver": "^1.11",
|
||||
"phpmyadmin/coding-standard": "^3.0.0",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
@ -98,7 +101,6 @@
|
||||
"pragmarx/google2fa-qrcode": "^2.1",
|
||||
"psalm/plugin-phpunit": "^0.16.1",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"code-lts/u2f-php-server": "^1.2",
|
||||
"symfony/console": "^5.2.3",
|
||||
"tecnickcom/tcpdf": "^6.4.4",
|
||||
"vimeo/psalm": "^4.17"
|
||||
|
||||
@ -147,6 +147,13 @@ $cfg['SaveDir'] = '';
|
||||
*/
|
||||
//$cfg['SendErrorReports'] = 'always';
|
||||
|
||||
/**
|
||||
* 'URLQueryEncryption' defines whether phpMyAdmin will encrypt sensitive data from the URL query string.
|
||||
* 'URLQueryEncryptionSecretKey' is a 32 bytes long secret key used to encrypt/decrypt the URL query string.
|
||||
*/
|
||||
//$cfg['URLQueryEncryption'] = true;
|
||||
//$cfg['URLQueryEncryptionSecretKey'] = '';
|
||||
|
||||
/**
|
||||
* You can find more configuration options in the documentation
|
||||
* in the doc/ folder or at <https://docs.phpmyadmin.net/>.
|
||||
|
||||
@ -1498,6 +1498,20 @@ Server connection settings
|
||||
after logout (doesn't affect config authentication method). Should be
|
||||
absolute including protocol.
|
||||
|
||||
.. config:option:: $cfg['Servers'][$i]['hide_connection_errors']
|
||||
|
||||
:type: boolean
|
||||
:default: false
|
||||
|
||||
.. versionadded:: 4.9.8
|
||||
|
||||
Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.
|
||||
|
||||
.. note::
|
||||
|
||||
This error message can contain the target database server hostname or IP address,
|
||||
which may reveal information about your network to an attacker.
|
||||
|
||||
Generic settings
|
||||
----------------
|
||||
|
||||
@ -1787,6 +1801,27 @@ Generic settings
|
||||
When enabled, a user can drag a file in to their browser and phpMyAdmin will
|
||||
attempt to import the file.
|
||||
|
||||
.. config:option:: $cfg['URLQueryEncryption']
|
||||
|
||||
:type: boolean
|
||||
:default: false
|
||||
|
||||
.. versionadded:: 4.9.8
|
||||
|
||||
Define whether phpMyAdmin will encrypt sensitive data (like database name
|
||||
and table name) from the URL query string. Default is to not encrypt the URL
|
||||
query string.
|
||||
|
||||
.. config:option:: $cfg['URLQueryEncryptionSecretKey']
|
||||
|
||||
:type: string
|
||||
:default: ``''``
|
||||
|
||||
.. versionadded:: 4.9.8
|
||||
|
||||
A secret key used to encrypt/decrypt the URL query string.
|
||||
Should be 32 bytes long.
|
||||
|
||||
Cookie authentication options
|
||||
-----------------------------
|
||||
|
||||
|
||||
@ -165,7 +165,7 @@ Navigation.loadChildNodes = function (isNode, $expandElem, callback) {
|
||||
};
|
||||
}
|
||||
|
||||
$.get('index.php?route=/navigation&ajax_request=1', params, function (data) {
|
||||
$.post('index.php?route=/navigation&ajax_request=1', params, function (data) {
|
||||
if (typeof data !== 'undefined' && data.success === true) {
|
||||
$destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
|
||||
if (isNode) {
|
||||
|
||||
@ -120,6 +120,8 @@ final class Common
|
||||
Session::setUp($config, $errorHandler);
|
||||
}
|
||||
|
||||
$request = Core::populateRequestWithEncryptedQueryParams($request);
|
||||
|
||||
/**
|
||||
* init some variables LABEL_variables_init
|
||||
*/
|
||||
|
||||
@ -370,6 +370,20 @@ final class Settings
|
||||
*/
|
||||
public $IgnoreMultiSubmitErrors;
|
||||
|
||||
/**
|
||||
* Define whether phpMyAdmin will encrypt sensitive data from the URL query string.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $URLQueryEncryption;
|
||||
|
||||
/**
|
||||
* A secret key used to encrypt/decrypt the URL query string. Should be 32 bytes long.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $URLQueryEncryptionSecretKey;
|
||||
|
||||
/**
|
||||
* allow login to any user entered server in cookie based authentication
|
||||
*
|
||||
@ -1603,6 +1617,8 @@ final class Settings
|
||||
$this->LoginCookieDeleteAll = $this->setLoginCookieDeleteAll($settings);
|
||||
$this->UseDbSearch = $this->setUseDbSearch($settings);
|
||||
$this->IgnoreMultiSubmitErrors = $this->setIgnoreMultiSubmitErrors($settings);
|
||||
$this->URLQueryEncryption = $this->setURLQueryEncryption($settings);
|
||||
$this->URLQueryEncryptionSecretKey = $this->setURLQueryEncryptionSecretKey($settings);
|
||||
$this->AllowArbitraryServer = $this->setAllowArbitraryServer($settings);
|
||||
$this->ArbitraryServerRegexp = $this->setArbitraryServerRegexp($settings);
|
||||
$this->CaptchaMethod = $this->setCaptchaMethod($settings);
|
||||
@ -2338,6 +2354,30 @@ final class Settings
|
||||
return (bool) $settings['IgnoreMultiSubmitErrors'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $settings
|
||||
*/
|
||||
private function setURLQueryEncryption(array $settings): bool
|
||||
{
|
||||
if (! isset($settings['URLQueryEncryption'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (bool) $settings['URLQueryEncryption'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $settings
|
||||
*/
|
||||
private function setURLQueryEncryptionSecretKey(array $settings): string
|
||||
{
|
||||
if (! isset($settings['URLQueryEncryptionSecretKey'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return (string) $settings['URLQueryEncryptionSecretKey'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $settings
|
||||
*/
|
||||
|
||||
@ -502,6 +502,13 @@ final class Server
|
||||
*/
|
||||
public $tracking_add_drop_database;
|
||||
|
||||
/**
|
||||
* Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $hide_connection_errors;
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $server
|
||||
*/
|
||||
@ -565,6 +572,7 @@ final class Server
|
||||
$this->tracking_add_drop_view = $this->setTrackingAddDropView($server);
|
||||
$this->tracking_add_drop_table = $this->setTrackingAddDropTable($server);
|
||||
$this->tracking_add_drop_database = $this->setTrackingAddDropDatabase($server);
|
||||
$this->hide_connection_errors = $this->setHideConnectionErrors($server);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1375,4 +1383,16 @@ final class Server
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $server
|
||||
*/
|
||||
private function setHideConnectionErrors(array $server): bool
|
||||
{
|
||||
if (isset($server['hide_connection_errors'])) {
|
||||
return (bool) $server['hide_connection_errors'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,8 @@ class UserGroups
|
||||
'viewUsers' => 1,
|
||||
'userGroup' => $groupName,
|
||||
],
|
||||
''
|
||||
'',
|
||||
false
|
||||
);
|
||||
$userGroupVal['viewUsersIcon'] = Generator::getIcon('b_usrlist', __('View users'));
|
||||
|
||||
@ -117,7 +118,8 @@ class UserGroups
|
||||
'editUserGroup' => 1,
|
||||
'userGroup' => $groupName,
|
||||
],
|
||||
''
|
||||
'',
|
||||
false
|
||||
);
|
||||
$userGroupVal['editUsersIcon'] = Generator::getIcon('b_edit', __('Edit'));
|
||||
$userGroupsValues[] = $userGroupVal;
|
||||
|
||||
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin;
|
||||
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
@ -29,6 +30,7 @@ use function in_array;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function mb_strlen;
|
||||
use function mb_strpos;
|
||||
@ -977,4 +979,64 @@ class Core
|
||||
|
||||
return $containerBuilder;
|
||||
}
|
||||
|
||||
public static function populateRequestWithEncryptedQueryParams(ServerRequest $request): ServerRequest
|
||||
{
|
||||
$queryParams = $request->getQueryParams();
|
||||
$parsedBody = $request->getParsedBody();
|
||||
|
||||
unset($_GET['eq'], $_POST['eq'], $_REQUEST['eq']);
|
||||
|
||||
if (! isset($queryParams['eq']) && (! is_array($parsedBody) || ! isset($parsedBody['eq']))) {
|
||||
return $request;
|
||||
}
|
||||
|
||||
$encryptedQuery = '';
|
||||
if (
|
||||
is_array($parsedBody)
|
||||
&& isset($parsedBody['eq'])
|
||||
&& is_string($parsedBody['eq'])
|
||||
&& $parsedBody['eq'] !== ''
|
||||
) {
|
||||
$encryptedQuery = $parsedBody['eq'];
|
||||
unset($parsedBody['eq'], $queryParams['eq']);
|
||||
} elseif (isset($queryParams['eq']) && is_string($queryParams['eq']) && $queryParams['eq'] !== '') {
|
||||
$encryptedQuery = $queryParams['eq'];
|
||||
unset($queryParams['eq']);
|
||||
}
|
||||
|
||||
$decryptedQuery = null;
|
||||
if ($encryptedQuery !== '') {
|
||||
$decryptedQuery = Url::decryptQuery($encryptedQuery);
|
||||
}
|
||||
|
||||
if ($decryptedQuery === null) {
|
||||
$request = $request->withQueryParams($queryParams);
|
||||
if (is_array($parsedBody)) {
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
|
||||
$urlQueryParams = (array) json_decode($decryptedQuery);
|
||||
foreach ($urlQueryParams as $urlQueryParamKey => $urlQueryParamValue) {
|
||||
if (is_array($parsedBody)) {
|
||||
$parsedBody[$urlQueryParamKey] = $urlQueryParamValue;
|
||||
$_POST[$urlQueryParamKey] = $urlQueryParamValue;
|
||||
} else {
|
||||
$queryParams[$urlQueryParamKey] = $urlQueryParamValue;
|
||||
$_GET[$urlQueryParamKey] = $urlQueryParamValue;
|
||||
}
|
||||
|
||||
$_REQUEST[$urlQueryParamKey] = $urlQueryParamValue;
|
||||
}
|
||||
|
||||
$request = $request->withQueryParams($queryParams);
|
||||
if (is_array($parsedBody)) {
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
}
|
||||
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
||||
67
libraries/classes/Crypto/Crypto.php
Normal file
67
libraries/classes/Crypto/Crypto.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Crypto;
|
||||
|
||||
use Throwable;
|
||||
|
||||
use function is_string;
|
||||
use function mb_strlen;
|
||||
use function mb_substr;
|
||||
use function random_bytes;
|
||||
use function sodium_crypto_secretbox;
|
||||
use function sodium_crypto_secretbox_open;
|
||||
|
||||
use const SODIUM_CRYPTO_SECRETBOX_KEYBYTES;
|
||||
use const SODIUM_CRYPTO_SECRETBOX_NONCEBYTES;
|
||||
|
||||
final class Crypto
|
||||
{
|
||||
private function getEncryptionKey(): string
|
||||
{
|
||||
global $config;
|
||||
|
||||
$key = $config->get('URLQueryEncryptionSecretKey');
|
||||
if (is_string($key) && mb_strlen($key, '8bit') === SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$key = $_SESSION['URLQueryEncryptionSecretKey'] ?? null;
|
||||
if (is_string($key) && mb_strlen($key, '8bit') === SODIUM_CRYPTO_SECRETBOX_KEYBYTES) {
|
||||
return $key;
|
||||
}
|
||||
|
||||
$key = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES);
|
||||
$_SESSION['URLQueryEncryptionSecretKey'] = $key;
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
public function encrypt(string $plaintext): string
|
||||
{
|
||||
$key = $this->getEncryptionKey();
|
||||
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
|
||||
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
|
||||
|
||||
return $nonce . $ciphertext;
|
||||
}
|
||||
|
||||
public function decrypt(string $encrypted): ?string
|
||||
{
|
||||
$key = $this->getEncryptionKey();
|
||||
$nonce = mb_substr($encrypted, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
|
||||
$ciphertext = mb_substr($encrypted, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
|
||||
try {
|
||||
$decrypted = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);
|
||||
} catch (Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! is_string($decrypted)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $decrypted;
|
||||
}
|
||||
}
|
||||
@ -19,9 +19,11 @@ use function mysqli_connect_error;
|
||||
use function mysqli_get_client_info;
|
||||
use function mysqli_init;
|
||||
use function mysqli_report;
|
||||
use function sprintf;
|
||||
use function stripos;
|
||||
use function trigger_error;
|
||||
|
||||
use const E_USER_ERROR;
|
||||
use const E_USER_WARNING;
|
||||
use const MYSQLI_CLIENT_COMPRESS;
|
||||
use const MYSQLI_CLIENT_SSL;
|
||||
@ -105,15 +107,27 @@ class DbiMysqli implements DbiExtension
|
||||
$host = $server['host'];
|
||||
}
|
||||
|
||||
$return_value = $mysqli->real_connect(
|
||||
$host,
|
||||
$user,
|
||||
$password,
|
||||
'',
|
||||
$server['port'],
|
||||
(string) $server['socket'],
|
||||
$client_flags
|
||||
);
|
||||
if ($server['hide_connection_errors']) {
|
||||
$return_value = @$mysqli->real_connect(
|
||||
$host,
|
||||
$user,
|
||||
$password,
|
||||
'',
|
||||
$server['port'],
|
||||
(string) $server['socket'],
|
||||
$client_flags
|
||||
);
|
||||
} else {
|
||||
$return_value = $mysqli->real_connect(
|
||||
$host,
|
||||
$user,
|
||||
$password,
|
||||
'',
|
||||
$server['port'],
|
||||
(string) $server['socket'],
|
||||
$client_flags
|
||||
);
|
||||
}
|
||||
|
||||
if ($return_value === false) {
|
||||
/*
|
||||
@ -143,6 +157,20 @@ class DbiMysqli implements DbiExtension
|
||||
return self::connect($user, $password, $server);
|
||||
}
|
||||
|
||||
if ($error_number === 1045 && $server['hide_connection_errors']) {
|
||||
trigger_error(
|
||||
sprintf(
|
||||
__(
|
||||
'Error 1045: Access denied for user. Additional error information'
|
||||
. ' may be available, but is being hidden by the %s configuration directive.'
|
||||
),
|
||||
'[code][doc@cfg_Servers_hide_connection_errors]'
|
||||
. '$cfg[\'Servers\'][$i][\'hide_connection_errors\'][/doc][/code]'
|
||||
),
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1475,9 +1475,8 @@ class Results
|
||||
$tmpImage = '<img class="fulltext" src="'
|
||||
. ($theme instanceof Theme ? $theme->getImgPath($tmpImageFile) : '')
|
||||
. '" alt="' . $tmpTxt . '" title="' . $tmpTxt . '">';
|
||||
$tmpUrl = Url::getFromRoute('/sql', $urlParamsFullText);
|
||||
|
||||
return Generator::linkOrButton($tmpUrl, $tmpImage);
|
||||
return Generator::linkOrButton(Url::getFromRoute('/sql'), $urlParamsFullText, $tmpImage);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1591,12 +1590,10 @@ class Results
|
||||
'session_max_rows' => $sessionMaxRows,
|
||||
'is_browse_distinct' => $this->properties['is_browse_distinct'],
|
||||
];
|
||||
$singleOrderUrl = Url::getFromRoute('/sql', $singleUrlParams);
|
||||
$multiOrderUrl = Url::getFromRoute('/sql', $multiUrlParams);
|
||||
|
||||
// Displays the sorting URL
|
||||
// enable sort order swapping for image
|
||||
$orderLink = $this->getSortOrderLink($orderImg, $fieldsMeta, $singleOrderUrl, $multiOrderUrl);
|
||||
$orderLink = $this->getSortOrderLink($orderImg, $fieldsMeta, $singleUrlParams, $multiUrlParams);
|
||||
|
||||
$orderLink .= $this->getSortOrderHiddenInputs($multiUrlParams, $fieldsMeta->name);
|
||||
|
||||
@ -1857,23 +1854,32 @@ class Results
|
||||
*
|
||||
* @see getTableHeaders()
|
||||
*
|
||||
* @param string $orderImg the sort order image
|
||||
* @param FieldMetadata $fieldsMeta set of field properties
|
||||
* @param string $orderUrl the url for sort
|
||||
* @param string $multiOrderUrl the url for sort
|
||||
* @param string $orderImg the sort order image
|
||||
* @param FieldMetadata $fieldsMeta set of field properties
|
||||
* @param array<int|string, mixed> $orderUrlParams the url params for sort
|
||||
* @param array<int|string, mixed> $multiOrderUrlParams the url params for sort
|
||||
*
|
||||
* @return string the sort order link
|
||||
*/
|
||||
private function getSortOrderLink(
|
||||
string $orderImg,
|
||||
FieldMetadata $fieldsMeta,
|
||||
string $orderUrl,
|
||||
string $multiOrderUrl
|
||||
array $orderUrlParams,
|
||||
array $multiOrderUrlParams
|
||||
): string {
|
||||
$urlPath = Url::getFromRoute('/sql');
|
||||
$innerLinkContent = htmlspecialchars($fieldsMeta->name) . $orderImg
|
||||
. '<input type="hidden" value="' . $multiOrderUrl . '">';
|
||||
. '<input type="hidden" value="'
|
||||
. $urlPath
|
||||
. Url::getCommon($multiOrderUrlParams, str_contains($urlPath, '?') ? '&' : '?', false)
|
||||
. '">';
|
||||
|
||||
return Generator::linkOrButton($orderUrl, $innerLinkContent, ['class' => 'sortlink']);
|
||||
return Generator::linkOrButton(
|
||||
Url::getFromRoute('/sql'),
|
||||
$orderUrlParams,
|
||||
$innerLinkContent,
|
||||
['class' => 'sortlink']
|
||||
);
|
||||
}
|
||||
|
||||
private function getSortOrderHiddenInputs(
|
||||
@ -2236,6 +2242,8 @@ class Results
|
||||
$copyUrl = null;
|
||||
$copyString = null;
|
||||
$editUrl = null;
|
||||
$editCopyUrlParams = null;
|
||||
$delUrlParams = null;
|
||||
|
||||
// 1.2 Defines the URLs for the modify/delete link(s)
|
||||
|
||||
@ -2277,11 +2285,12 @@ class Results
|
||||
$copyUrl,
|
||||
$editString,
|
||||
$copyString,
|
||||
$editCopyUrlParams,
|
||||
] = $this->getModifiedLinks($whereClause, $clauseIsUnique, $urlSqlQuery);
|
||||
}
|
||||
|
||||
// 1.2.2 Delete/Kill link(s)
|
||||
[$deleteUrl, $deleteString, $jsConf] = $this->getDeleteAndKillLinks(
|
||||
[$deleteUrl, $deleteString, $jsConf, $delUrlParams] = $this->getDeleteAndKillLinks(
|
||||
$whereClause,
|
||||
$clauseIsUnique,
|
||||
$urlSqlQuery,
|
||||
@ -2297,9 +2306,18 @@ class Results
|
||||
$tableBodyHtml .= $this->template->render('display/results/checkbox_and_links', [
|
||||
'position' => self::POSITION_LEFT,
|
||||
'has_checkbox' => $deleteUrl && $displayParts['del_lnk'] !== self::KILL_PROCESS,
|
||||
'edit' => ['url' => $editUrl, 'string' => $editString, 'clause_is_unique' => $clauseIsUnique],
|
||||
'copy' => ['url' => $copyUrl, 'string' => $copyString],
|
||||
'delete' => ['url' => $deleteUrl, 'string' => $deleteString],
|
||||
'edit' => [
|
||||
'url' => $editUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'update'],
|
||||
'string' => $editString,
|
||||
'clause_is_unique' => $clauseIsUnique,
|
||||
],
|
||||
'copy' => [
|
||||
'url' => $copyUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'insert'],
|
||||
'string' => $copyString,
|
||||
],
|
||||
'delete' => ['url' => $deleteUrl, 'params' => $delUrlParams, 'string' => $deleteString],
|
||||
'row_number' => $rowNumber,
|
||||
'where_clause' => $whereClause,
|
||||
'condition' => json_encode($conditionArray),
|
||||
@ -2310,9 +2328,18 @@ class Results
|
||||
$tableBodyHtml .= $this->template->render('display/results/checkbox_and_links', [
|
||||
'position' => self::POSITION_NONE,
|
||||
'has_checkbox' => $deleteUrl && $displayParts['del_lnk'] !== self::KILL_PROCESS,
|
||||
'edit' => ['url' => $editUrl, 'string' => $editString, 'clause_is_unique' => $clauseIsUnique],
|
||||
'copy' => ['url' => $copyUrl, 'string' => $copyString],
|
||||
'delete' => ['url' => $deleteUrl, 'string' => $deleteString],
|
||||
'edit' => [
|
||||
'url' => $editUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'update'],
|
||||
'string' => $editString,
|
||||
'clause_is_unique' => $clauseIsUnique,
|
||||
],
|
||||
'copy' => [
|
||||
'url' => $copyUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'insert'],
|
||||
'string' => $copyString,
|
||||
],
|
||||
'delete' => ['url' => $deleteUrl, 'params' => $delUrlParams, 'string' => $deleteString],
|
||||
'row_number' => $rowNumber,
|
||||
'where_clause' => $whereClause,
|
||||
'condition' => json_encode($conditionArray),
|
||||
@ -2350,11 +2377,16 @@ class Results
|
||||
'has_checkbox' => $deleteUrl && $displayParts['del_lnk'] !== self::KILL_PROCESS,
|
||||
'edit' => [
|
||||
'url' => $editUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'update'],
|
||||
'string' => $editString,
|
||||
'clause_is_unique' => $clauseIsUnique ?? true,
|
||||
],
|
||||
'copy' => ['url' => $copyUrl, 'string' => $copyString],
|
||||
'delete' => ['url' => $deleteUrl, 'string' => $deleteString],
|
||||
'copy' => [
|
||||
'url' => $copyUrl,
|
||||
'params' => $editCopyUrlParams + ['default_action' => 'insert'],
|
||||
'string' => $copyString,
|
||||
],
|
||||
'delete' => ['url' => $deleteUrl, 'params' => $delUrlParams, 'string' => $deleteString],
|
||||
'row_number' => $rowNumber,
|
||||
'where_clause' => $whereClause ?? '',
|
||||
'condition' => json_encode($conditionArray ?? []),
|
||||
@ -2904,8 +2936,7 @@ class Results
|
||||
* @param bool $clauseIsUnique the unique condition of clause
|
||||
* @param string $urlSqlQuery the analyzed sql query
|
||||
*
|
||||
* @return array<int,string> 5 element array - $edit_url, $copy_url,
|
||||
* $edit_str, $copy_str
|
||||
* @return array<int,string|array<string, bool|string>>
|
||||
*/
|
||||
private function getModifiedLinks(
|
||||
$whereClause,
|
||||
@ -2921,15 +2952,9 @@ class Results
|
||||
'goto' => Url::getFromRoute('/sql'),
|
||||
];
|
||||
|
||||
$editUrl = Url::getFromRoute(
|
||||
'/table/change',
|
||||
$urlParams + ['default_action' => 'update']
|
||||
);
|
||||
$editUrl = Url::getFromRoute('/table/change');
|
||||
|
||||
$copyUrl = Url::getFromRoute(
|
||||
'/table/change',
|
||||
$urlParams + ['default_action' => 'insert']
|
||||
);
|
||||
$copyUrl = Url::getFromRoute('/table/change');
|
||||
|
||||
$editStr = $this->getActionLinkContent(
|
||||
'b_edit',
|
||||
@ -2945,6 +2970,7 @@ class Results
|
||||
$copyUrl,
|
||||
$editStr,
|
||||
$copyStr,
|
||||
$urlParams,
|
||||
];
|
||||
}
|
||||
|
||||
@ -2994,7 +3020,7 @@ class Results
|
||||
'message_to_show' => __('The row has been deleted.'),
|
||||
'goto' => $linkGoto,
|
||||
];
|
||||
$deleteUrl = Url::getFromRoute('/sql', $urlParams);
|
||||
$deleteUrl = Url::getFromRoute('/sql');
|
||||
|
||||
$jsConf = 'DELETE FROM ' . $this->properties['table']
|
||||
. ' WHERE ' . $whereClause
|
||||
@ -3019,20 +3045,21 @@ class Results
|
||||
'goto' => $linkGoto,
|
||||
];
|
||||
|
||||
$deleteUrl = Url::getFromRoute('/sql', $urlParams);
|
||||
$deleteUrl = Url::getFromRoute('/sql');
|
||||
$jsConf = $kill;
|
||||
$deleteString = Generator::getIcon(
|
||||
'b_drop',
|
||||
__('Kill')
|
||||
);
|
||||
} else {
|
||||
$deleteUrl = $deleteString = $jsConf = null;
|
||||
$deleteUrl = $deleteString = $jsConf = $urlParams = null;
|
||||
}
|
||||
|
||||
return [
|
||||
$deleteUrl,
|
||||
$deleteString,
|
||||
$jsConf,
|
||||
$urlParams,
|
||||
];
|
||||
}
|
||||
|
||||
@ -4441,7 +4468,8 @@ class Results
|
||||
}
|
||||
|
||||
$value .= Generator::linkOrButton(
|
||||
Url::getFromRoute('/sql', $urlParams),
|
||||
Url::getFromRoute('/sql'),
|
||||
$urlParams,
|
||||
$displayedData,
|
||||
$tagParams
|
||||
);
|
||||
|
||||
@ -203,6 +203,7 @@ class ErrorReport
|
||||
if (isset($components['query'])) {
|
||||
parse_str($components['query'], $queryArray);
|
||||
unset($queryArray['db'], $queryArray['table'], $queryArray['token'], $queryArray['server']);
|
||||
unset($queryArray['eq']);
|
||||
$query = http_build_query($queryArray);
|
||||
} else {
|
||||
$query = '';
|
||||
|
||||
@ -547,16 +547,18 @@ class Export
|
||||
*/
|
||||
$backButton = '<p>[ <a href="';
|
||||
if ($exportType === 'server') {
|
||||
$backButton .= Url::getFromRoute('/server/export') . '" data-post="' . Url::getCommon([], '');
|
||||
$backButton .= Url::getFromRoute('/server/export') . '" data-post="' . Url::getCommon([], '', false);
|
||||
} elseif ($exportType === 'database') {
|
||||
$backButton .= Url::getFromRoute('/database/export') . '" data-post="' . Url::getCommon(['db' => $db], '');
|
||||
$backButton .= Url::getFromRoute('/database/export') . '" data-post="' . Url::getCommon(
|
||||
['db' => $db],
|
||||
'',
|
||||
false
|
||||
);
|
||||
} else {
|
||||
$backButton .= Url::getFromRoute('/table/export') . '" data-post="' . Url::getCommon(
|
||||
[
|
||||
'db' => $db,
|
||||
'table' => $table,
|
||||
],
|
||||
''
|
||||
['db' => $db, 'table' => $table],
|
||||
'',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -627,14 +627,16 @@ class Generator
|
||||
$explainParams['sql_query'] = 'EXPLAIN ' . $sqlQuery;
|
||||
$explainLink = ' [ '
|
||||
. self::linkOrButton(
|
||||
Url::getFromRoute('/import', $explainParams),
|
||||
Url::getFromRoute('/import'),
|
||||
$explainParams,
|
||||
__('Explain SQL')
|
||||
) . ' ]';
|
||||
} elseif (preg_match('@^EXPLAIN[[:space:]]+SELECT[[:space:]]+@i', $sqlQuery)) {
|
||||
$explainParams['sql_query'] = mb_substr($sqlQuery, 8);
|
||||
$explainLink = ' [ '
|
||||
. self::linkOrButton(
|
||||
Url::getFromRoute('/import', $explainParams),
|
||||
Url::getFromRoute('/import'),
|
||||
$explainParams,
|
||||
__('Skip Explain SQL')
|
||||
) . ']';
|
||||
$url = 'https://mariadb.org/explain_analyzer/analyze/'
|
||||
@ -643,6 +645,7 @@ class Generator
|
||||
$explainLink .= ' ['
|
||||
. self::linkOrButton(
|
||||
htmlspecialchars('url.php?url=' . urlencode($url)),
|
||||
null,
|
||||
sprintf(__('Analyze Explain at %s'), 'mariadb.org'),
|
||||
[],
|
||||
'_blank',
|
||||
@ -657,9 +660,8 @@ class Generator
|
||||
// even if the query is big and was truncated, offer the chance
|
||||
// to edit it (unless it's enormous, see linkOrButton() )
|
||||
if (! empty($cfg['SQLQuery']['Edit']) && empty($GLOBALS['show_as_php'])) {
|
||||
$editLink .= Url::getCommon($urlParams, '&');
|
||||
$editLink = ' [ '
|
||||
. self::linkOrButton($editLink, __('Edit'))
|
||||
. self::linkOrButton($editLink, $urlParams, __('Edit'))
|
||||
. ' ]';
|
||||
} else {
|
||||
$editLink = '';
|
||||
@ -671,14 +673,16 @@ class Generator
|
||||
if (! empty($GLOBALS['show_as_php'])) {
|
||||
$phpLink = ' [ '
|
||||
. self::linkOrButton(
|
||||
Url::getFromRoute('/import', $urlParams),
|
||||
Url::getFromRoute('/import'),
|
||||
$urlParams,
|
||||
__('Without PHP code')
|
||||
)
|
||||
. ' ]';
|
||||
|
||||
$phpLink .= ' [ '
|
||||
. self::linkOrButton(
|
||||
Url::getFromRoute('/import', $urlParams),
|
||||
Url::getFromRoute('/import'),
|
||||
$urlParams,
|
||||
__('Submit query')
|
||||
)
|
||||
. ' ]';
|
||||
@ -687,7 +691,8 @@ class Generator
|
||||
$phpParams['show_as_php'] = 1;
|
||||
$phpLink = ' [ '
|
||||
. self::linkOrButton(
|
||||
Url::getFromRoute('/import', $phpParams),
|
||||
Url::getFromRoute('/import'),
|
||||
$phpParams,
|
||||
__('Create PHP code')
|
||||
)
|
||||
. ' ]';
|
||||
@ -704,7 +709,7 @@ class Generator
|
||||
) {
|
||||
$refreshLink = Url::getFromRoute('/sql', $urlParams);
|
||||
$refreshLink = ' [ '
|
||||
. self::linkOrButton($refreshLink, __('Refresh')) . ' ]';
|
||||
. self::linkOrButton($refreshLink, $urlParams, __('Refresh')) . ' ]';
|
||||
} else {
|
||||
$refreshLink = '';
|
||||
}
|
||||
@ -737,6 +742,7 @@ class Generator
|
||||
$inlineEditLink = ' [ '
|
||||
. self::linkOrButton(
|
||||
'#',
|
||||
null,
|
||||
_pgettext('Inline edit query', 'Edit inline'),
|
||||
['class' => 'inline_edit_sql']
|
||||
)
|
||||
@ -1038,21 +1044,28 @@ class Generator
|
||||
* - URL components are over Suhosin limits
|
||||
* - There is SQL query in the parameters
|
||||
*
|
||||
* @param string $url the URL
|
||||
* @param string $message the link message
|
||||
* @param mixed $tagParams string: js confirmation; array: additional tag
|
||||
* params (f.e. style="")
|
||||
* @param string $target target
|
||||
* @param string $urlPath the URL
|
||||
* @param array<int|string, mixed>|null $urlParams URL parameters
|
||||
* @param string $message the link message
|
||||
* @param string|array<string, string> $tagParams string: js confirmation;
|
||||
* array: additional tag params (f.e. style="")
|
||||
* @param string $target target
|
||||
*
|
||||
* @return string the results to be echoed or saved in an array
|
||||
*/
|
||||
public static function linkOrButton(
|
||||
$url,
|
||||
$urlPath,
|
||||
$urlParams,
|
||||
$message,
|
||||
$tagParams = [],
|
||||
$target = '',
|
||||
bool $respectUrlLengthLimit = true
|
||||
): string {
|
||||
$url = $urlPath;
|
||||
if (is_array($urlParams)) {
|
||||
$url = $urlPath . Url::getCommon($urlParams, str_contains($urlPath, '?') ? '&' : '?', false);
|
||||
}
|
||||
|
||||
$urlLength = strlen($url);
|
||||
|
||||
if (! is_array($tagParams)) {
|
||||
@ -1114,6 +1127,11 @@ class Generator
|
||||
if (array_key_exists('class', $tagParams) && str_contains($tagParams['class'], 'create_view')) {
|
||||
$url .= '?' . explode('&', $parts[1], 2)[0];
|
||||
}
|
||||
} else {
|
||||
$url = $urlPath;
|
||||
if (is_array($urlParams)) {
|
||||
$url = $urlPath . Url::getCommon($urlParams, str_contains($urlPath, '?') ? '&' : '?');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($tagParams as $paramName => $paramValue) {
|
||||
|
||||
@ -317,13 +317,13 @@ class InsertEdit
|
||||
|
||||
if (! $isShow) {
|
||||
return ' : <a href="' . Url::getFromRoute('/table/change') . '" data-post="'
|
||||
. Url::getCommon($thisUrlParams, '') . '">'
|
||||
. Url::getCommon($thisUrlParams, '', false) . '">'
|
||||
. $this->showTypeOrFunctionLabel($which)
|
||||
. '</a>';
|
||||
}
|
||||
|
||||
return '<th><a href="' . Url::getFromRoute('/table/change') . '" data-post="'
|
||||
. Url::getCommon($thisUrlParams, '')
|
||||
. Url::getCommon($thisUrlParams, '', false)
|
||||
. '" title="' . __('Hide') . '">'
|
||||
. $this->showTypeOrFunctionLabel($which)
|
||||
. '</a></th>';
|
||||
|
||||
@ -152,13 +152,13 @@ class NavigationTree
|
||||
}
|
||||
|
||||
// Get the active node
|
||||
if (isset($_REQUEST['aPath'])) {
|
||||
$this->aPath[0] = $this->parsePath($_REQUEST['aPath']);
|
||||
$this->pos2Name[0] = $_REQUEST['pos2_name'] ?? '';
|
||||
$this->pos2Value[0] = (int) ($_REQUEST['pos2_value'] ?? 0);
|
||||
if (isset($_REQUEST['pos3_name'])) {
|
||||
$this->pos3Name[0] = $_REQUEST['pos3_name'] ?? '';
|
||||
$this->pos3Value[0] = (int) $_REQUEST['pos3_value'];
|
||||
if (isset($_POST['aPath'])) {
|
||||
$this->aPath[0] = $this->parsePath($_POST['aPath']);
|
||||
$this->pos2Name[0] = $_POST['pos2_name'] ?? '';
|
||||
$this->pos2Value[0] = (int) ($_POST['pos2_value'] ?? 0);
|
||||
if (isset($_POST['pos3_name'])) {
|
||||
$this->pos3Name[0] = $_POST['pos3_name'] ?? '';
|
||||
$this->pos3Value[0] = (int) $_POST['pos3_value'];
|
||||
}
|
||||
} else {
|
||||
if (isset($_POST['n0_aPath'])) {
|
||||
@ -180,8 +180,8 @@ class NavigationTree
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['vPath'])) {
|
||||
$this->vPath[0] = $this->parsePath($_REQUEST['vPath']);
|
||||
if (isset($_POST['vPath'])) {
|
||||
$this->vPath[0] = $this->parsePath($_POST['vPath']);
|
||||
} else {
|
||||
if (isset($_POST['n0_vPath'])) {
|
||||
$count = 0;
|
||||
@ -192,12 +192,12 @@ class NavigationTree
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['searchClause'])) {
|
||||
$this->searchClause = $_REQUEST['searchClause'];
|
||||
if (isset($_POST['searchClause'])) {
|
||||
$this->searchClause = $_POST['searchClause'];
|
||||
}
|
||||
|
||||
if (isset($_REQUEST['searchClause2'])) {
|
||||
$this->searchClause2 = $_REQUEST['searchClause2'];
|
||||
if (isset($_POST['searchClause2'])) {
|
||||
$this->searchClause2 = $_POST['searchClause2'];
|
||||
}
|
||||
|
||||
// Initialize the tree by creating a root node
|
||||
|
||||
@ -657,7 +657,7 @@ class NodeDatabase extends Node
|
||||
];
|
||||
$ret = '<span class="dbItemControls">'
|
||||
. '<a href="' . Url::getFromRoute('/navigation') . '" data-post="'
|
||||
. Url::getCommon($params, '') . '"'
|
||||
. Url::getCommon($params, '', false) . '"'
|
||||
. ' class="showUnhide ajax">'
|
||||
. Generator::getImage(
|
||||
'show',
|
||||
|
||||
@ -48,7 +48,7 @@ abstract class NodeDatabaseChild extends Node
|
||||
|
||||
$ret = '<span class="navItemControls">'
|
||||
. '<a href="' . Url::getFromRoute('/navigation') . '" data-post="'
|
||||
. Url::getCommon($params, '') . '"'
|
||||
. Url::getCommon($params, '', false) . '"'
|
||||
. ' class="hideNavItem ajax">'
|
||||
. Generator::getImage('hide', __('Hide'))
|
||||
. '</a></span>';
|
||||
|
||||
@ -139,7 +139,7 @@ class ReplicationGui
|
||||
}
|
||||
|
||||
$urlParams['sr_replica_control_param'] = 'IO_THREAD';
|
||||
$replicaControlIoLink = Url::getCommon($urlParams, '');
|
||||
$replicaControlIoLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
if ($serverReplicaReplication[0]['Slave_SQL_Running'] === 'No') {
|
||||
$urlParams['sr_replica_action'] = 'start';
|
||||
@ -148,7 +148,7 @@ class ReplicationGui
|
||||
}
|
||||
|
||||
$urlParams['sr_replica_control_param'] = 'SQL_THREAD';
|
||||
$replicaControlSqlLink = Url::getCommon($urlParams, '');
|
||||
$replicaControlSqlLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
if (
|
||||
$serverReplicaReplication[0]['Slave_IO_Running'] === 'No'
|
||||
@ -160,21 +160,21 @@ class ReplicationGui
|
||||
}
|
||||
|
||||
$urlParams['sr_replica_control_param'] = null;
|
||||
$replicaControlFullLink = Url::getCommon($urlParams, '');
|
||||
$replicaControlFullLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
$urlParams['sr_replica_action'] = 'reset';
|
||||
$replicaControlResetLink = Url::getCommon($urlParams, '');
|
||||
$replicaControlResetLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
$urlParams = $GLOBALS['urlParams'];
|
||||
$urlParams['sr_take_action'] = true;
|
||||
$urlParams['sr_replica_skip_error'] = true;
|
||||
$replicaSkipErrorLink = Url::getCommon($urlParams, '');
|
||||
$replicaSkipErrorLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
$urlParams = $GLOBALS['urlParams'];
|
||||
$urlParams['replica_configure'] = true;
|
||||
$urlParams['repl_clear_scr'] = true;
|
||||
|
||||
$reconfigurePrimaryLink = Url::getCommon($urlParams, '');
|
||||
$reconfigurePrimaryLink = Url::getCommon($urlParams, '', false);
|
||||
|
||||
$replicaStatusTable = $this->getHtmlForReplicationStatusTable('replica', true, false);
|
||||
|
||||
|
||||
@ -7,13 +7,20 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin;
|
||||
|
||||
use PhpMyAdmin\Crypto\Crypto;
|
||||
|
||||
use function base64_decode;
|
||||
use function base64_encode;
|
||||
use function htmlentities;
|
||||
use function htmlspecialchars;
|
||||
use function http_build_query;
|
||||
use function in_array;
|
||||
use function ini_get;
|
||||
use function is_array;
|
||||
use function json_encode;
|
||||
use function str_contains;
|
||||
use function strlen;
|
||||
use function strtr;
|
||||
|
||||
/**
|
||||
* Static methods for URL/hidden inputs generating
|
||||
@ -164,12 +171,13 @@ class Url
|
||||
*
|
||||
* @param array<string,int|string|bool> $params optional, Contains an associative array with url params
|
||||
* @param string $divider optional character to use instead of '?'
|
||||
* @param bool $encrypt whether to encrypt URL params
|
||||
*
|
||||
* @return string string with URL parameters
|
||||
*/
|
||||
public static function getCommon(array $params = [], $divider = '?')
|
||||
public static function getCommon(array $params = [], $divider = '?', $encrypt = true)
|
||||
{
|
||||
return self::getCommonRaw($params, $divider);
|
||||
return self::getCommonRaw($params, $divider, $encrypt);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -197,15 +205,14 @@ class Url
|
||||
*
|
||||
* @param array<string|int,int|string|bool> $params optional, Contains an associative array with url params
|
||||
* @param string $divider optional character to use instead of '?'
|
||||
* @param bool $encrypt whether to encrypt URL params
|
||||
*
|
||||
* @return string string with URL parameters
|
||||
*/
|
||||
public static function getCommonRaw(array $params = [], $divider = '?')
|
||||
public static function getCommonRaw(array $params = [], $divider = '?', $encrypt = true)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$separator = self::getArgSeparator();
|
||||
|
||||
// avoid overwriting when creating navigation panel links to servers
|
||||
if (
|
||||
isset($GLOBALS['server'])
|
||||
@ -221,7 +228,7 @@ class Url
|
||||
$params['lang'] = $GLOBALS['lang'];
|
||||
}
|
||||
|
||||
$query = http_build_query($params, '', $separator);
|
||||
$query = self::buildHttpQuery($params, $encrypt);
|
||||
|
||||
if (($divider !== '?' && $divider !== '&') || strlen($query) > 0) {
|
||||
return $divider . $query;
|
||||
@ -230,6 +237,71 @@ class Url
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int|string, mixed> $params
|
||||
* @param bool $encrypt whether to encrypt URL params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function buildHttpQuery($params, $encrypt = true)
|
||||
{
|
||||
global $config;
|
||||
|
||||
$separator = self::getArgSeparator();
|
||||
|
||||
if (! $encrypt || ! $config->get('URLQueryEncryption')) {
|
||||
return http_build_query($params, '', $separator);
|
||||
}
|
||||
|
||||
$data = $params;
|
||||
$keys = [
|
||||
'db',
|
||||
'table',
|
||||
'field',
|
||||
'sql_query',
|
||||
'sql_signature',
|
||||
'where_clause',
|
||||
'goto',
|
||||
'back',
|
||||
'message_to_show',
|
||||
'username',
|
||||
'hostname',
|
||||
'dbname',
|
||||
'tablename',
|
||||
'checkprivsdb',
|
||||
'checkprivstable',
|
||||
];
|
||||
$paramsToEncrypt = [];
|
||||
foreach ($params as $paramKey => $paramValue) {
|
||||
if (! in_array($paramKey, $keys)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$paramsToEncrypt[$paramKey] = $paramValue;
|
||||
unset($data[$paramKey]);
|
||||
}
|
||||
|
||||
if ($paramsToEncrypt !== []) {
|
||||
$data['eq'] = self::encryptQuery((string) json_encode($paramsToEncrypt));
|
||||
}
|
||||
|
||||
return http_build_query($data, '', $separator);
|
||||
}
|
||||
|
||||
public static function encryptQuery(string $query): string
|
||||
{
|
||||
$crypto = new Crypto();
|
||||
|
||||
return strtr(base64_encode($crypto->encrypt($query)), '+/', '-_');
|
||||
}
|
||||
|
||||
public static function decryptQuery(string $query): ?string
|
||||
{
|
||||
$crypto = new Crypto();
|
||||
|
||||
return $crypto->decrypt(base64_decode(strtr($query, '-_', '+/')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns url separator
|
||||
*
|
||||
|
||||
@ -2631,9 +2631,9 @@ class Util
|
||||
$urlParams['tbl_group'] = $_REQUEST['tbl_group'];
|
||||
}
|
||||
|
||||
$url = Url::getFromRoute('/database/structure', $urlParams);
|
||||
$url = Url::getFromRoute('/database/structure');
|
||||
|
||||
return Generator::linkOrButton($url, $title . $orderImg, $orderLinkParams);
|
||||
return Generator::linkOrButton($url, $urlParams, $title . $orderImg, $orderLinkParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -606,6 +606,13 @@ $cfg['Servers'][$i]['tracking_add_drop_table'] = true;
|
||||
*/
|
||||
$cfg['Servers'][$i]['tracking_add_drop_database'] = true;
|
||||
|
||||
/**
|
||||
* Whether to show or hide detailed MySQL/MariaDB connection errors on the login page.
|
||||
*
|
||||
* @global bool $cfg['Servers'][$i]['hide_connection_errors']
|
||||
*/
|
||||
$cfg['Servers'][$i]['hide_connection_errors'] = false;
|
||||
|
||||
/**
|
||||
* Default server (0 = no default server)
|
||||
*
|
||||
@ -839,6 +846,20 @@ $cfg['UseDbSearch'] = true;
|
||||
*/
|
||||
$cfg['IgnoreMultiSubmitErrors'] = false;
|
||||
|
||||
/**
|
||||
* Define whether phpMyAdmin will encrypt sensitive data from the URL query string.
|
||||
*
|
||||
* @global bool $cfg['URLQueryEncryption']
|
||||
*/
|
||||
$cfg['URLQueryEncryption'] = false;
|
||||
|
||||
/**
|
||||
* A secret key used to encrypt/decrypt the URL query string. Should be 32 bytes long.
|
||||
*
|
||||
* @global string $cfg['URLQueryEncryptionSecretKey']
|
||||
*/
|
||||
$cfg['URLQueryEncryptionSecretKey'] = '';
|
||||
|
||||
/**
|
||||
* allow login to any user entered server in cookie based authentication
|
||||
*
|
||||
|
||||
@ -385,6 +385,11 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Config/Form.php
|
||||
|
||||
-
|
||||
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Config/Form.php
|
||||
|
||||
-
|
||||
message: "#^Argument of an invalid type mixed supplied for foreach, only iterables are supported\\.$#"
|
||||
count: 1
|
||||
@ -632,7 +637,7 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Cannot cast mixed to string\\.$#"
|
||||
count: 43
|
||||
count: 44
|
||||
path: libraries/classes/Config/Settings.php
|
||||
|
||||
-
|
||||
@ -2880,6 +2885,16 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Binary operation \"\\+\" between array\\<string, bool\\|string\\>\\|string\\|null and array\\{default_action\\: 'insert'\\} results in an error\\.$#"
|
||||
count: 3
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Binary operation \"\\+\" between array\\<string, bool\\|string\\>\\|string\\|null and array\\{default_action\\: 'update'\\} results in an error\\.$#"
|
||||
count: 3
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access offset int on mixed\\.$#"
|
||||
count: 2
|
||||
@ -3225,11 +3240,21 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Offset 3 does not exist on array\\{string\\|null, string\\|null, string\\|null\\}\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$offset of method PhpMyAdmin\\\\Dbal\\\\ResultInterface\\:\\:seek\\(\\) expects int, float\\|int given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$params of static method PhpMyAdmin\\\\Url\\:\\:getCommon\\(\\) expects array\\<string, bool\\|int\\|string\\>, array\\<int\\|string, mixed\\> given\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Display/Results.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#3 \\$colOrder of method PhpMyAdmin\\\\Display\\\\Results\\:\\:getRowValues\\(\\) expects array\\|false, mixed given\\.$#"
|
||||
count: 1
|
||||
@ -4341,8 +4366,8 @@ parameters:
|
||||
path: libraries/classes/Html/Generator.php
|
||||
|
||||
-
|
||||
message: "#^Parameter \\#1 \\$string of static method PhpMyAdmin\\\\Sanitize\\:\\:escapeJsString\\(\\) expects string, mixed given\\.$#"
|
||||
count: 1
|
||||
message: "#^Parameter \\#1 \\$params of static method PhpMyAdmin\\\\Url\\:\\:getCommon\\(\\) expects array\\<string, bool\\|int\\|string\\>, array\\<int\\|string, mixed\\> given\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/Html/Generator.php
|
||||
|
||||
-
|
||||
@ -8410,6 +8435,16 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/Theme.php
|
||||
|
||||
-
|
||||
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/Theme.php
|
||||
|
||||
-
|
||||
message: "#^If condition is always true\\.$#"
|
||||
count: 1
|
||||
path: libraries/classes/ThemeManager.php
|
||||
|
||||
-
|
||||
message: "#^Method PhpMyAdmin\\\\ThemeManager\\:\\:getThemesArray\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||
count: 1
|
||||
@ -8425,6 +8460,11 @@ parameters:
|
||||
count: 1
|
||||
path: libraries/classes/ThemeManager.php
|
||||
|
||||
-
|
||||
message: "#^Unreachable statement \\- code above always terminates\\.$#"
|
||||
count: 2
|
||||
path: libraries/classes/ThemeManager.php
|
||||
|
||||
-
|
||||
message: "#^Cannot access property \\$table on PhpMyAdmin\\\\SqlParser\\\\Components\\\\Expression\\|string\\.$#"
|
||||
count: 1
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<files psalm-version="4.17.0@6f4707aa41c9174353a6434bba3fc8840f981d9c">
|
||||
<files psalm-version="4.18.1@dda05fa913f4dc6eb3386f2f7ce5a45d37a71bcb">
|
||||
<file src="index.php">
|
||||
<InvalidGlobal occurrences="1">
|
||||
<code>global $route, $containerBuilder, $request;</code>
|
||||
@ -4127,15 +4127,13 @@
|
||||
<MixedArgumentTypeCoercion occurrences="1">
|
||||
<code>['db' => $db]</code>
|
||||
</MixedArgumentTypeCoercion>
|
||||
<MixedArrayAccess occurrences="8">
|
||||
<MixedArrayAccess occurrences="6">
|
||||
<code>$_POST['view']['as']</code>
|
||||
<code>$_POST['view']['as']</code>
|
||||
<code>$_POST['view']['column_names']</code>
|
||||
<code>$_POST['view']['column_names']</code>
|
||||
<code>$_POST['view']['name']</code>
|
||||
<code>$_POST['view']['name']</code>
|
||||
<code>$_POST['view']['with']</code>
|
||||
<code>$_POST['view']['with']</code>
|
||||
</MixedArrayAccess>
|
||||
<MixedArrayAssignment occurrences="4">
|
||||
<code>$urlParams['back']</code>
|
||||
@ -4218,16 +4216,22 @@
|
||||
<MixedArrayOffset occurrences="1">
|
||||
<code>$GLOBALS['cfg']['TrustedProxies'][$direct_ip]</code>
|
||||
</MixedArrayOffset>
|
||||
<MixedAssignment occurrences="10">
|
||||
<MixedAssignment occurrences="16">
|
||||
<code>$GLOBALS[$post_key]</code>
|
||||
<code>$_GET[$urlQueryParamKey]</code>
|
||||
<code>$_POST[$urlQueryParamKey]</code>
|
||||
<code>$_REQUEST[$urlQueryParamKey]</code>
|
||||
<code>$a</code>
|
||||
<code>$a[$last_key]</code>
|
||||
<code>$direct_ip</code>
|
||||
<code>$one_post_pattern</code>
|
||||
<code>$parsedBody[$urlQueryParamKey]</code>
|
||||
<code>$path[$depth]</code>
|
||||
<code>$query</code>
|
||||
<code>$queryParams[$urlQueryParamKey]</code>
|
||||
<code>$secret</code>
|
||||
<code>$secret</code>
|
||||
<code>$urlQueryParamValue</code>
|
||||
<code>$value</code>
|
||||
</MixedAssignment>
|
||||
<MixedInferredReturnType occurrences="2">
|
||||
@ -4360,6 +4364,15 @@
|
||||
<code>$_POST['field_extra'][$i] ?? false</code>
|
||||
</PossiblyFalseArgument>
|
||||
</file>
|
||||
<file src="libraries/classes/Crypto/Crypto.php">
|
||||
<MixedAssignment occurrences="2">
|
||||
<code>$key</code>
|
||||
<code>$key</code>
|
||||
</MixedAssignment>
|
||||
<MixedMethodCall occurrences="1">
|
||||
<code>get</code>
|
||||
</MixedMethodCall>
|
||||
</file>
|
||||
<file src="libraries/classes/Database/CentralColumns.php">
|
||||
<InvalidScalarArgument occurrences="1">
|
||||
<code>$tn_pageNow</code>
|
||||
@ -5622,8 +5635,10 @@
|
||||
</MixedAssignment>
|
||||
</file>
|
||||
<file src="libraries/classes/Dbal/DbiMysqli.php">
|
||||
<MixedArgument occurrences="7">
|
||||
<MixedArgument occurrences="9">
|
||||
<code>$host</code>
|
||||
<code>$host</code>
|
||||
<code>$server['port']</code>
|
||||
<code>$server['port']</code>
|
||||
<code>$server['ssl_ca'] ?? ''</code>
|
||||
<code>$server['ssl_ca_path'] ?? ''</code>
|
||||
@ -5685,9 +5700,13 @@
|
||||
<code>$displayParts['nav_bar'] == '1' && $statement !== null && empty($statement->limit)</code>
|
||||
<code>[]</code>
|
||||
</DocblockTypeContradiction>
|
||||
<InvalidArgument occurrences="1">
|
||||
<InvalidArgument occurrences="2">
|
||||
<code>$added[$orgFullTableName]</code>
|
||||
<code>$multiOrderUrlParams</code>
|
||||
</InvalidArgument>
|
||||
<InvalidArrayOffset occurrences="1">
|
||||
<code>$delUrlParams</code>
|
||||
</InvalidArrayOffset>
|
||||
<MixedArgument occurrences="53">
|
||||
<code>$_SESSION['tmpval']['max_rows']</code>
|
||||
<code>$_SESSION['tmpval']['pos'] / $_SESSION['tmpval']['max_rows']</code>
|
||||
@ -5994,14 +6013,28 @@
|
||||
<code>$this->properties['num_rows'] > 0 ? $this->properties['num_rows'] - 1 : 0</code>
|
||||
<code>$this->properties['num_rows'] > 0 ? $this->properties['num_rows'] - 1 : 0</code>
|
||||
</PossiblyInvalidArgument>
|
||||
<PossiblyInvalidOperand occurrences="6">
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
</PossiblyInvalidOperand>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$colVisibCurrent</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullArrayAccess occurrences="1">
|
||||
<code>$firstStatement->order[$key]</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
<PossiblyNullOperand occurrences="2">
|
||||
<PossiblyNullOperand occurrences="8">
|
||||
<code>$dispval</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$editCopyUrlParams</code>
|
||||
<code>$this->getSortedColumnMessage($dtResult, $sortExpressionNoDirection[$i])</code>
|
||||
</PossiblyNullOperand>
|
||||
<RedundantCast occurrences="3">
|
||||
@ -7306,18 +7339,21 @@
|
||||
</TypeDoesNotContainNull>
|
||||
</file>
|
||||
<file src="libraries/classes/Html/Generator.php">
|
||||
<InvalidArgument occurrences="2">
|
||||
<code>$urlParams</code>
|
||||
<code>$urlParams</code>
|
||||
</InvalidArgument>
|
||||
<InvalidArrayOffset occurrences="2">
|
||||
<code>$server['ssl']</code>
|
||||
<code>$server['ssl_verify']</code>
|
||||
</InvalidArrayOffset>
|
||||
<MixedArgument occurrences="23">
|
||||
<MixedArgument occurrences="20">
|
||||
<code>$GLOBALS['special_message']</code>
|
||||
<code>$GLOBALS['special_message']</code>
|
||||
<code>$alt</code>
|
||||
<code>$cfg['MaxCharactersInDisplayedSQL']</code>
|
||||
<code>$defaultFunction</code>
|
||||
<code>$field['True_Type']</code>
|
||||
<code>$paramValue</code>
|
||||
<code>$queryBase</code>
|
||||
<code>$queryBase</code>
|
||||
<code>$queryBase</code>
|
||||
@ -7330,15 +7366,12 @@
|
||||
<code>$sqlQuery</code>
|
||||
<code>$sqlQuery</code>
|
||||
<code>$subvalue</code>
|
||||
<code>$tagParams['class']</code>
|
||||
<code>$title</code>
|
||||
<code>$tmp</code>
|
||||
<code>$value</code>
|
||||
</MixedArgument>
|
||||
<MixedArgumentTypeCoercion occurrences="3">
|
||||
<MixedArgumentTypeCoercion occurrences="2">
|
||||
<code>$classes</code>
|
||||
<code>$key</code>
|
||||
<code>$urlParams</code>
|
||||
</MixedArgumentTypeCoercion>
|
||||
<MixedArrayAccess occurrences="3">
|
||||
<code>$cfg['DefaultFunctions']['FUNC_' . $currentClass]</code>
|
||||
@ -7351,17 +7384,15 @@
|
||||
<code>$_SESSION['tmpval']['max_rows']</code>
|
||||
<code>$_SESSION['tmpval']['pos']</code>
|
||||
</MixedArrayAssignment>
|
||||
<MixedAssignment occurrences="14">
|
||||
<MixedAssignment occurrences="12">
|
||||
<code>$alt</code>
|
||||
<code>$defaultFunction</code>
|
||||
<code>$defaultFunction</code>
|
||||
<code>$defaultFunction</code>
|
||||
<code>$paramValue</code>
|
||||
<code>$queryBase</code>
|
||||
<code>$sqlQuery</code>
|
||||
<code>$subvalue</code>
|
||||
<code>$title</code>
|
||||
<code>$tmp</code>
|
||||
<code>$urlParams['sql_query']</code>
|
||||
<code>$value</code>
|
||||
<code>$value</code>
|
||||
@ -8104,10 +8135,10 @@
|
||||
<code>isset($this->pos)</code>
|
||||
</DocblockTypeContradiction>
|
||||
<MixedArgument occurrences="34">
|
||||
<code>$_POST['aPath']</code>
|
||||
<code>$_POST['n' . $count . '_aPath']</code>
|
||||
<code>$_POST['n' . $count . '_vPath']</code>
|
||||
<code>$_REQUEST['aPath']</code>
|
||||
<code>$_REQUEST['vPath']</code>
|
||||
<code>$_POST['vPath']</code>
|
||||
<code>$container->children</code>
|
||||
<code>$container->realName</code>
|
||||
<code>$db</code>
|
||||
@ -9014,12 +9045,12 @@
|
||||
<code>$propertyItem->getName()</code>
|
||||
<code>$subgroup_header->getName()</code>
|
||||
</PossiblyNullOperand>
|
||||
<RedundantCast occurrences="1">
|
||||
<code>strtolower</code>
|
||||
</RedundantCast>
|
||||
<RedundantConditionGivenDocblockType occurrences="1">
|
||||
<code>$properties != null</code>
|
||||
</RedundantConditionGivenDocblockType>
|
||||
<RedundantFunctionCall occurrences="1">
|
||||
<code>strtolower</code>
|
||||
</RedundantFunctionCall>
|
||||
<UnnecessaryVarAnnotation occurrences="3">
|
||||
<code>RadioPropertyItem</code>
|
||||
<code>SelectPropertyItem</code>
|
||||
@ -13401,12 +13432,11 @@
|
||||
<MixedArgumentTypeCoercion occurrences="1">
|
||||
<code>$values</code>
|
||||
</MixedArgumentTypeCoercion>
|
||||
<MixedArrayAccess occurrences="5">
|
||||
<MixedArrayAccess occurrences="4">
|
||||
<code>$_POST['criteriaColumnNames'][$column_index]</code>
|
||||
<code>$_POST['criteriaColumnTypes'][$column_index]</code>
|
||||
<code>$_POST['criteriaValues'][$column_index]</code>
|
||||
<code>$_POST['geom_func'][$column_index]</code>
|
||||
<code>$criteriaValues[0]</code>
|
||||
</MixedArrayAccess>
|
||||
<MixedArrayOffset occurrences="4">
|
||||
<code>$_POST['criteriaColumnNames'][$column_index]</code>
|
||||
@ -13798,7 +13828,9 @@
|
||||
<MixedArrayOffset occurrences="1">
|
||||
<code>$params[$skipping]</code>
|
||||
</MixedArrayOffset>
|
||||
<MixedAssignment occurrences="3">
|
||||
<MixedAssignment occurrences="5">
|
||||
<code>$paramValue</code>
|
||||
<code>$paramsToEncrypt[$paramKey]</code>
|
||||
<code>$skipping</code>
|
||||
<code>$value</code>
|
||||
<code>$values['token']</code>
|
||||
@ -13806,7 +13838,8 @@
|
||||
<MixedInferredReturnType occurrences="1">
|
||||
<code>string</code>
|
||||
</MixedInferredReturnType>
|
||||
<MixedMethodCall occurrences="3">
|
||||
<MixedMethodCall occurrences="4">
|
||||
<code>get</code>
|
||||
<code>get</code>
|
||||
<code>getCookie</code>
|
||||
<code>getCookie</code>
|
||||
@ -14666,6 +14699,9 @@
|
||||
</UndefinedMethod>
|
||||
</file>
|
||||
<file src="test/classes/CoreTest.php">
|
||||
<InvalidArgument occurrences="1">
|
||||
<code>testPopulateRequestWithEncryptedQueryParamsWithInvalidParam</code>
|
||||
</InvalidArgument>
|
||||
<MixedArgument occurrences="9">
|
||||
<code>$arr['arr']</code>
|
||||
<code>$arr['arr']</code>
|
||||
@ -14703,6 +14739,12 @@
|
||||
<code>array</code>
|
||||
<code>array</code>
|
||||
</MixedInferredReturnType>
|
||||
<MixedMethodCall occurrences="4">
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
</MixedMethodCall>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$page</code>
|
||||
</PossiblyNullArgument>
|
||||
@ -14717,6 +14759,21 @@
|
||||
<code>testGetColumnCreationQuery</code>
|
||||
</PossiblyInvalidArgument>
|
||||
</file>
|
||||
<file src="test/classes/Crypto/CryptoTest.php">
|
||||
<EmptyArrayAccess occurrences="1">
|
||||
<code>$_SESSION['URLQueryEncryptionSecretKey']</code>
|
||||
</EmptyArrayAccess>
|
||||
<MixedArgument occurrences="1">
|
||||
<code>$_SESSION['URLQueryEncryptionSecretKey']</code>
|
||||
</MixedArgument>
|
||||
<MixedMethodCall occurrences="5">
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
</MixedMethodCall>
|
||||
</file>
|
||||
<file src="test/classes/Database/CentralColumnsTest.php">
|
||||
<MixedArgument occurrences="1"/>
|
||||
<MixedArrayAccess occurrences="1">
|
||||
@ -16060,6 +16117,15 @@
|
||||
<code>array</code>
|
||||
</MixedInferredReturnType>
|
||||
</file>
|
||||
<file src="test/classes/UrlTest.php">
|
||||
<MixedMethodCall occurrences="5">
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
<code>set</code>
|
||||
</MixedMethodCall>
|
||||
</file>
|
||||
<file src="test/classes/UserPreferencesTest.php">
|
||||
<MixedArgument occurrences="1">
|
||||
<code>$_SESSION['userconfig']</code>
|
||||
|
||||
@ -89,11 +89,12 @@
|
||||
<td>
|
||||
{% if has_privilege %}
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'db': db,
|
||||
'sql_query': 'DROP EVENT IF EXISTS %s'|format(backquote(event.name)),
|
||||
'goto': url('/database/events', {'db': db})
|
||||
}),
|
||||
},
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'ajax drop_anchor'}
|
||||
) }}
|
||||
|
||||
@ -39,12 +39,13 @@
|
||||
<td>
|
||||
{% if has_privilege %}
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'db': db,
|
||||
'table': table,
|
||||
'sql_query': sql_drop,
|
||||
'goto': url('/database/events', {'db': db})
|
||||
}),
|
||||
},
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'ajax drop_anchor'}
|
||||
) }}
|
||||
|
||||
@ -84,7 +84,8 @@
|
||||
<div class="card-body">
|
||||
<div class="card-text">
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'sql_query': 'DROP DATABASE ' ~ backquote(db),
|
||||
'back': url('/database/operations'),
|
||||
'goto': url('/'),
|
||||
@ -92,7 +93,7 @@
|
||||
'purge': true,
|
||||
'message_to_show': 'Database %s has been dropped.'|trans|format(backquote(db))|e,
|
||||
'db': null
|
||||
}),
|
||||
},
|
||||
'Drop the database (DROP)'|trans,
|
||||
{
|
||||
'id': 'drop_db_anchor',
|
||||
|
||||
@ -69,12 +69,13 @@
|
||||
</td>
|
||||
<td>
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'db': db,
|
||||
'table': table,
|
||||
'sql_query': sql_drop,
|
||||
'goto': url('/database/routines', {'db': db})
|
||||
}),
|
||||
},
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'ajax drop_anchor'}
|
||||
) }}
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
'back': url('/database/tracking'),
|
||||
'table': version.table_name,
|
||||
'delete_tracking': true
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_drop', 'Delete tracking'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -100,7 +100,7 @@
|
||||
'goto': url('/table/tracking'),
|
||||
'back': url('/database/tracking'),
|
||||
'table': version.table_name
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_versions', 'Versions'|trans) }}
|
||||
</a>
|
||||
<a href="{{ url('/table/tracking') }}" data-post="
|
||||
@ -111,7 +111,7 @@
|
||||
'table': version.table_name,
|
||||
'report': true,
|
||||
'version': version.version
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_report', 'Tracking report'|trans) }}
|
||||
</a>
|
||||
<a href="{{ url('/table/tracking') }}" data-post="
|
||||
@ -122,7 +122,7 @@
|
||||
'table': version.table_name,
|
||||
'snapshot': true,
|
||||
'version': version.version
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_props', 'Structure snapshot'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@ -44,12 +44,13 @@
|
||||
<td>
|
||||
{% if has_drop_privilege %}
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'db': db,
|
||||
'table': table,
|
||||
'sql_query': trigger.drop,
|
||||
'goto': url('/database/triggers', {'db': db})
|
||||
}),
|
||||
},
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'ajax drop_anchor'}
|
||||
) }}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
{% if edit.url is not empty %}
|
||||
<td class="text-center d-print-none edit_row_anchor{{ not edit.clause_is_unique ? ' nonunique' }}">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(edit.url, edit.string) }}
|
||||
{{ link_or_button(edit.url, edit.params, edit.string) }}
|
||||
{% if where_clause is not empty %}
|
||||
<input type="hidden" class="where_clause" value="{{ where_clause }}">
|
||||
{% endif %}
|
||||
@ -21,7 +21,7 @@
|
||||
{% if copy.url is not empty %}
|
||||
<td class="text-center d-print-none">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(copy.url, copy.string) }}
|
||||
{{ link_or_button(copy.url, copy.params, copy.string) }}
|
||||
{% if where_clause is not empty %}
|
||||
<input type="hidden" class="where_clause" value="{{ where_clause }}">
|
||||
{% endif %}
|
||||
@ -32,7 +32,7 @@
|
||||
{% if delete.url is not empty %}
|
||||
<td class="text-center d-print-none{{ is_ajax ? ' ajax' }}">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(delete.url, delete.string, {'class': 'delete_row requireConfirm' ~ (is_ajax ? ' ajax') }) }}
|
||||
{{ link_or_button(delete.url, delete.params, delete.string, {'class': 'delete_row requireConfirm' ~ (is_ajax ? ' ajax') }) }}
|
||||
{% if js_conf is not empty %}
|
||||
<div class="hide">{{ js_conf }}</div>
|
||||
{% endif %}
|
||||
@ -43,7 +43,7 @@
|
||||
{% if delete.url is not empty %}
|
||||
<td class="text-center d-print-none{{ is_ajax ? ' ajax' }}">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(delete.url, delete.string, {'class': 'delete_row requireConfirm' ~ (is_ajax ? ' ajax') }) }}
|
||||
{{ link_or_button(delete.url, delete.params, delete.string, {'class': 'delete_row requireConfirm' ~ (is_ajax ? ' ajax') }) }}
|
||||
{% if js_conf is not empty %}
|
||||
<div class="hide">{{ js_conf }}</div>
|
||||
{% endif %}
|
||||
@ -54,7 +54,7 @@
|
||||
{% if copy.url is not empty %}
|
||||
<td class="text-center d-print-none">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(copy.url, copy.string) }}
|
||||
{{ link_or_button(copy.url, copy.params, copy.string) }}
|
||||
{% if where_clause is not empty %}
|
||||
<input type="hidden" class="where_clause" value="{{ where_clause }}">
|
||||
{% endif %}
|
||||
@ -65,7 +65,7 @@
|
||||
{% if edit.url is not empty %}
|
||||
<td class="text-center d-print-none edit_row_anchor{{ not edit.clause_is_unique ? ' nonunique' }}">
|
||||
<span class="text-nowrap">
|
||||
{{ link_or_button(edit.url, edit.string) }}
|
||||
{{ link_or_button(edit.url, edit.params, edit.string) }}
|
||||
{% if where_clause is not empty %}
|
||||
<input type="hidden" class="where_clause" value="{{ where_clause }}">
|
||||
{% endif %}
|
||||
|
||||
@ -259,6 +259,7 @@
|
||||
|
||||
{{ link_or_button(
|
||||
'#',
|
||||
null,
|
||||
get_icon('b_insrow', 'Copy to clipboard'|trans, true),
|
||||
{'id': 'copyToClipBoard', 'class': 'btn'}
|
||||
) }}
|
||||
@ -267,20 +268,23 @@
|
||||
{% if not operations.has_procedure %}
|
||||
{% if operations.has_export_link %}
|
||||
{{ link_or_button(
|
||||
url('/table/export', operations.url_params),
|
||||
url('/table/export'),
|
||||
operations.url_params,
|
||||
get_icon('b_tblexport', 'Export'|trans, true),
|
||||
{'class': 'btn'}
|
||||
) }}
|
||||
|
||||
{{ link_or_button(
|
||||
url('/table/chart', operations.url_params),
|
||||
url('/table/chart'),
|
||||
operations.url_params,
|
||||
get_icon('b_chart', 'Display chart'|trans, true),
|
||||
{'class': 'btn'}
|
||||
) }}
|
||||
|
||||
{% if operations.has_geometry %}
|
||||
{{ link_or_button(
|
||||
url('/table/gis-visualization', operations.url_params),
|
||||
url('/table/gis-visualization'),
|
||||
operations.url_params,
|
||||
get_icon('b_globe', 'Visualize GIS data'|trans, true),
|
||||
{'class': 'btn'}
|
||||
) }}
|
||||
@ -289,7 +293,8 @@
|
||||
|
||||
<span>
|
||||
{{ link_or_button(
|
||||
url('/view/create', {'db': db, 'table': table, 'sql_query': sql_query, 'printview': true}),
|
||||
url('/view/create'),
|
||||
{'db': db, 'table': table, 'sql_query': sql_query, 'printview': true},
|
||||
get_icon('b_view_add', 'Create view'|trans, true),
|
||||
{'class': 'btn create_view ajax'}
|
||||
) }}
|
||||
|
||||
@ -30,12 +30,12 @@
|
||||
{% set columns_count = index.getColumnCount() %}
|
||||
<tr class="noclick">
|
||||
<td rowspan="{{ columns_count }}" class="edit_index d-print-none ajax">
|
||||
<a class="ajax" href="{{ url('/table/indexes') }}" data-post="{{ get_common(url_params|merge({'index': index.getName()}), '') }}">
|
||||
<a class="ajax" href="{{ url('/table/indexes') }}" data-post="{{ get_common(url_params|merge({'index': index.getName()}), '', false) }}">
|
||||
{{ get_icon('b_edit', 'Edit'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
<td rowspan="{{ columns_count }}" class="rename_index d-print-none ajax" >
|
||||
<a class="ajax" href="{{ url('/table/indexes/rename') }}" data-post="{{ get_common(url_params|merge({'index': index.getName()}), '') }}">
|
||||
<a class="ajax" href="{{ url('/table/indexes/rename') }}" data-post="{{ get_common(url_params|merge({'index': index.getName()}), '', false) }}">
|
||||
{{ get_icon('b_rename', 'Rename'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -54,7 +54,8 @@
|
||||
|
||||
<input type="hidden" class="drop_primary_key_index_msg" value="{{ index_params.sql_query }}">
|
||||
{{ link_or_button(
|
||||
url('/sql', url_params|merge(index_params)),
|
||||
url('/sql'),
|
||||
url_params|merge(index_params),
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'drop_primary_key_index_anchor ajax'}
|
||||
) }}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
'itemType': type,
|
||||
'itemName': item,
|
||||
'dbName': database
|
||||
}, '') }}">{{ get_icon('show', 'Unhide'|trans) }}</a>
|
||||
}, '', false) }}">{{ get_icon('show', 'Unhide'|trans) }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
@ -43,12 +43,12 @@
|
||||
<td colspan="6" class="text-center">
|
||||
{% if has_previous %}
|
||||
{% if has_icons %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(previous_params, '') }}" title="
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(previous_params, '', false) }}" title="
|
||||
{%- trans %}Previous{% context %}Previous page{% endtrans %}">
|
||||
«
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(previous_params, '') }}">
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(previous_params, '', false) }}">
|
||||
{% trans %}Previous{% context %}Previous page{% endtrans %} «
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -56,11 +56,11 @@
|
||||
{% endif %}
|
||||
|
||||
{% if is_full_query %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(full_queries_params, '') }}" title="{% trans 'Truncate shown queries' %}">
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(full_queries_params, '', false) }}" title="{% trans 'Truncate shown queries' %}">
|
||||
<img src="{{ image('s_partialtext.png') }}" alt="{% trans 'Truncate shown queries' %}">
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(full_queries_params, '') }}" title="{% trans 'Show full queries' %}">
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(full_queries_params, '', false) }}" title="{% trans 'Show full queries' %}">
|
||||
<img src="{{ image('s_fulltext.png') }}" alt="{% trans 'Show full queries' %}">
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -68,12 +68,12 @@
|
||||
{% if has_next %}
|
||||
-
|
||||
{% if has_icons %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(next_params, '') }}" title="
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(next_params, '', false) }}" title="
|
||||
{%- trans %}Next{% context %}Next page{% endtrans %}">
|
||||
»
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(next_params, '') }}">
|
||||
<a href="{{ url('/server/binlog') }}" data-post="{{ get_common(next_params, '', false) }}">
|
||||
{% trans %}Next{% context %}Next page{% endtrans %} »
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
@ -297,7 +297,7 @@
|
||||
<div class="alert alert-info" role="alert">
|
||||
{{ get_icon('s_notice', 'Note: Enabling the database statistics here might cause heavy traffic between the web server and the MySQL server.'|trans) }}
|
||||
</div>
|
||||
<a class="card-link" href="{{ url('/server/databases') }}" data-post="{{ get_common({'statistics': '1'}, '') }}" title="{% trans 'Enable statistics' %}">
|
||||
<a class="card-link" href="{{ url('/server/databases') }}" data-post="{{ get_common({'statistics': '1'}, '', false) }}" title="{% trans 'Enable statistics' %}">
|
||||
{% trans 'Enable statistics' %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<div class="card mb-2">
|
||||
<div class="card-header">{% trans 'Primary replication' %}</div>
|
||||
<div class="card-body">
|
||||
{% apply format('<a href="' ~ url('/server/replication') ~ '" data-post="' ~ get_common(url_params|merge({'primary_configure': true}), '') ~ '">', '</a>')|raw %}
|
||||
{% apply format('<a href="' ~ url('/server/replication') ~ '" data-post="' ~ get_common(url_params|merge({'primary_configure': true}), '', false) ~ '">', '</a>')|raw %}
|
||||
{% trans %}
|
||||
This server is not configured as primary in a replication process. Would you like to %sconfigure%s it?
|
||||
{% endtrans %}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
<th>{% trans 'Processes' %}</th>
|
||||
{% for column in columns %}
|
||||
<th scope="col">
|
||||
<a href="{{ url('/server/status/processes') }}" data-post="{{ get_common(column.params) }}" class="sortlink">
|
||||
<a href="{{ url('/server/status/processes') }}" data-post="{{ get_common(column.params, '', false) }}" class="sortlink">
|
||||
{{ column.name }}
|
||||
{% if column.is_sorted %}
|
||||
<img class="icon ic_s_desc soimg" alt="
|
||||
@ -15,7 +15,7 @@
|
||||
{% endif %}
|
||||
</a>
|
||||
{% if column.has_full_query %}
|
||||
<a href="{{ url('/server/status/processes') }}" data-post="{{ get_common(refresh_params, '') }}">
|
||||
<a href="{{ url('/server/status/processes') }}" data-post="{{ get_common(refresh_params, '', false) }}">
|
||||
{% if column.is_full %}
|
||||
{{ get_image(
|
||||
's_partialtext',
|
||||
@ -40,7 +40,7 @@
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
<td>
|
||||
<a class="ajax kill_process" href="{{ url('/server/status/processes/kill/' ~ row.id) }}" data-post="{{ get_common({'kill': row.id}, '') }}">
|
||||
<a class="ajax kill_process" href="{{ url('/server/status/processes/kill/' ~ row.id) }}" data-post="{{ get_common({'kill': row.id}, '', false) }}">
|
||||
{% trans 'Kill' %}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
</a>
|
||||
|
|
||||
<a class="delete-server" href="{{ get_common(server.params.remove) }}" data-post="
|
||||
{{- get_common({ token: server.params.token }, '') }}">
|
||||
{{- get_common({ token: server.params.token }, '', false) }}">
|
||||
{% trans 'Delete' %}
|
||||
</a>
|
||||
</small>
|
||||
|
||||
@ -9,7 +9,8 @@
|
||||
<legend>{% trans 'Query results operations' %}</legend>
|
||||
<span>
|
||||
{{ link_or_button(
|
||||
url('/view/create', {'db': db, 'table': table, 'printview': '1', 'sql_query': sql_query}),
|
||||
url('/view/create'),
|
||||
{'db': db, 'table': table, 'printview': '1', 'sql_query': sql_query},
|
||||
get_icon('b_view_add', 'Create view'|trans, true),
|
||||
{'class': 'create_view ajax btn'}
|
||||
) }}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<span class="curr_value">{{ current_value }}</span>
|
||||
<a href="{{ url('/browse-foreigners') }}" data-post="{{ get_common(params, '') }}" class="ajax browse_foreign">
|
||||
<a href="{{ url('/browse-foreigners') }}" data-post="{{ get_common(params, '', false) }}" class="ajax browse_foreign">
|
||||
{% trans 'Browse foreign values' %}
|
||||
</a>
|
||||
|
||||
@ -119,7 +119,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if column.pma_type in gis_data_types %}
|
||||
<span class="open_gis_editor" data-row-id="{{ row_id }}">{{ link_or_button('#', get_icon('b_edit', 'Edit/Insert'|trans), [], '_blank') }}</span>
|
||||
<span class="open_gis_editor" data-row-id="{{ row_id }}">{{ link_or_button('#', null, get_icon('b_edit', 'Edit/Insert'|trans), [], '_blank') }}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@ -335,7 +335,7 @@
|
||||
<ul class="list-group list-group-flush" id="tbl_maintenance">
|
||||
{% if storage_engine in ['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'] %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ url('/table/maintenance/analyze') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}) }}">
|
||||
<a href="{{ url('/table/maintenance/analyze') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}, '', false) }}">
|
||||
{% trans 'Analyze table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('ANALYZE_TABLE') }}
|
||||
@ -344,7 +344,7 @@
|
||||
|
||||
{% if storage_engine in ['MYISAM', 'ARIA', 'INNODB', 'TOKUDB'] %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ url('/table/maintenance/check') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}) }}">
|
||||
<a href="{{ url('/table/maintenance/check') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}, '', false) }}">
|
||||
{% trans 'Check table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('CHECK_TABLE') }}
|
||||
@ -352,7 +352,7 @@
|
||||
{% endif %}
|
||||
|
||||
<li class="list-group-item">
|
||||
<a href="{{ url('/table/maintenance/checksum') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}) }}">
|
||||
<a href="{{ url('/table/maintenance/checksum') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}, '', false) }}">
|
||||
{% trans 'Checksum table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('CHECKSUM_TABLE') }}
|
||||
@ -360,7 +360,7 @@
|
||||
|
||||
{% if storage_engine == 'INNODB' %}
|
||||
<li class="list-group-item">
|
||||
<a class="maintain_action ajax" href="{{ url('/sql') }}" data-post="{{ get_common(url_params|merge({'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ENGINE = InnoDB;'})) }}">
|
||||
<a class="maintain_action ajax" href="{{ url('/sql') }}" data-post="{{ get_common(url_params|merge({'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ENGINE = InnoDB;'}), '', false) }}">
|
||||
{% trans 'Defragment table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('InnoDB_File_Defragmenting') }}
|
||||
@ -372,7 +372,7 @@
|
||||
'sql_query': 'FLUSH TABLE ' ~ backquote(table),
|
||||
'message_to_show': 'Table %s has been flushed.'|trans|format(table|e),
|
||||
'reload': true
|
||||
})) }}">
|
||||
}), '', false) }}">
|
||||
{% trans 'Flush the table (FLUSH)' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('FLUSH') }}
|
||||
@ -380,7 +380,7 @@
|
||||
|
||||
{% if storage_engine in ['MYISAM', 'ARIA', 'INNODB', 'BERKELEYDB', 'TOKUDB'] %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ url('/table/maintenance/optimize') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}) }}">
|
||||
<a href="{{ url('/table/maintenance/optimize') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}, '', false) }}">
|
||||
{% trans 'Optimize table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('OPTIMIZE_TABLE') }}
|
||||
@ -389,7 +389,7 @@
|
||||
|
||||
{% if storage_engine in ['MYISAM', 'ARIA'] %}
|
||||
<li class="list-group-item">
|
||||
<a href="{{ url('/table/maintenance/repair') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}) }}">
|
||||
<a href="{{ url('/table/maintenance/repair') }}" data-post="{{ get_common({'db': db, 'table': table, 'selected_tbl': [table]}, '', false) }}">
|
||||
{% trans 'Repair table' %}
|
||||
</a>
|
||||
{{ show_mysql_docu('REPAIR_TABLE') }}
|
||||
@ -405,12 +405,13 @@
|
||||
{% if not is_view %}
|
||||
<li class="list-group-item">
|
||||
{{ link_or_button(
|
||||
url('/sql', url_params|merge({
|
||||
url('/sql'),
|
||||
url_params|merge({
|
||||
'sql_query': 'TRUNCATE TABLE ' ~ backquote(db) ~ '.' ~ backquote(table),
|
||||
'goto': url('/table/structure'),
|
||||
'reload': true,
|
||||
'message_to_show': 'Table %s has been emptied.'|trans|format(table)|e
|
||||
})),
|
||||
}),
|
||||
'Empty the table (TRUNCATE)'|trans,
|
||||
{
|
||||
'id': 'truncate_tbl_anchor',
|
||||
@ -422,12 +423,13 @@
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
{{ link_or_button(
|
||||
url('/sql', url_params|merge({
|
||||
url('/sql'),
|
||||
url_params|merge({
|
||||
'sql_query': 'DELETE FROM ' ~ backquote(db) ~ '.' ~ backquote(table),
|
||||
'goto': url('/table/structure'),
|
||||
'reload': true,
|
||||
'message_to_show': 'Table %s has been emptied.'|trans|format(table)|e
|
||||
})),
|
||||
}),
|
||||
'Empty the table (DELETE FROM)'|trans,
|
||||
{
|
||||
'id': 'delete_tbl_anchor',
|
||||
@ -440,14 +442,15 @@
|
||||
{% endif %}
|
||||
<li class="list-group-item">
|
||||
{{ link_or_button(
|
||||
url('/sql', url_params|merge({
|
||||
url('/sql'),
|
||||
url_params|merge({
|
||||
'sql_query': 'DROP TABLE ' ~ backquote(db) ~ '.' ~ backquote(table),
|
||||
'goto': url('/database/operations'),
|
||||
'reload': true,
|
||||
'purge': true,
|
||||
'message_to_show': is_view ? 'View %s has been dropped.'|trans|format(table)|e : 'Table %s has been dropped.'|trans|format(table)|e,
|
||||
'table': table
|
||||
})),
|
||||
}),
|
||||
'Delete the table (DROP)'|trans,
|
||||
{
|
||||
'id': 'drop_tbl_anchor',
|
||||
|
||||
@ -27,14 +27,15 @@
|
||||
<div class="card-body">
|
||||
<div class="card-text">
|
||||
{{ link_or_button(
|
||||
url('/sql', url_params|merge({
|
||||
url('/sql'),
|
||||
url_params|merge({
|
||||
'sql_query': 'DROP VIEW ' ~ backquote(table),
|
||||
'goto': url('/table/structure'),
|
||||
'reload': true,
|
||||
'purge': true,
|
||||
'message_to_show': 'View %s has been dropped.'|trans|format(table)|e,
|
||||
'table': table
|
||||
})),
|
||||
}),
|
||||
'Delete the view (DROP)'|trans,
|
||||
{
|
||||
'id': 'drop_view_anchor',
|
||||
|
||||
@ -22,9 +22,8 @@
|
||||
{% if one_key['constraint'] is defined %}
|
||||
<input type="hidden" class="drop_foreign_key_msg" value="
|
||||
{{- js_msg }}">
|
||||
{% set drop_url = url('/sql', this_params) %}
|
||||
{% set drop_str = get_icon('b_drop', 'Drop'|trans) %}
|
||||
{{ link_or_button(drop_url, drop_str, {'class': 'drop_foreign_key_anchor ajax'}) }}
|
||||
{{ link_or_button(url('/sql'), this_params, drop_str, {'class': 'drop_foreign_key_anchor ajax'}) }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
value="{{ criteria_values[column_index] }}"
|
||||
{% endif %}>
|
||||
<a class="ajax browse_foreign" href="{{ url('/browse-foreigners') }}" data-post="
|
||||
{{- get_common({'db': db, 'table': table}, '') -}}
|
||||
{{- get_common({'db': db, 'table': table}, '', false) -}}
|
||||
&field={{ column_name|url_encode }}&fieldkey=
|
||||
{{- column_index }}&fromsearch=1">
|
||||
{{ get_icon('b_browse', 'Browse foreign values'|trans) }}
|
||||
@ -36,7 +36,7 @@
|
||||
{% if in_fbs %}
|
||||
{% set edit_str = get_icon('b_edit', 'Edit/Insert'|trans) %}
|
||||
<span class="open_search_gis_editor">
|
||||
{{ link_or_button(url('/gis-data-editor'), edit_str, [], '_blank') }}
|
||||
{{ link_or_button(url('/gis-data-editor'), [], edit_str, [], '_blank') }}
|
||||
</span>
|
||||
{% endif %}
|
||||
{% elseif column_type starts with 'enum'
|
||||
|
||||
@ -80,7 +80,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_search', 'Analyze'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -90,7 +90,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('eye', 'Check'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -100,7 +100,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('normalize', 'Optimize'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -110,7 +110,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('s_tbl', 'Rebuild'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -120,7 +120,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_tblops', 'Repair'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -130,7 +130,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_empty', 'Truncate'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -141,7 +141,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'partition_name': partition.getName(),
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_drop', 'Drop'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -194,11 +194,12 @@
|
||||
<input class="btn btn-secondary" type="submit" value="{% trans 'Partition table' %}">
|
||||
{% else %}
|
||||
{{ link_or_button(
|
||||
url('/sql', {
|
||||
url('/sql'),
|
||||
{
|
||||
'db': db,
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' REMOVE PARTITIONING'
|
||||
}),
|
||||
},
|
||||
'Remove partitioning'|trans, {
|
||||
'class': 'btn btn-secondary ajax',
|
||||
'id': 'remove_partitioning'
|
||||
|
||||
@ -116,7 +116,7 @@
|
||||
'dropped_column': row['Field'],
|
||||
'purge': true,
|
||||
'message_to_show': 'Column %s has been dropped.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_drop', 'Drop'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -141,7 +141,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ (primary ? ' DROP PRIMARY KEY,') ~ ' ADD PRIMARY KEY(' ~ backquote(row['Field']) ~ ');',
|
||||
'message_to_show': 'A primary key has been added on %s.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_primary', 'Primary'|trans) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -156,7 +156,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD UNIQUE(' ~ backquote(row['Field']) ~ ');',
|
||||
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_unique', 'Unique'|trans) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -171,7 +171,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD INDEX(' ~ backquote(row['Field']) ~ ');',
|
||||
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_index', 'Index'|trans) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -196,7 +196,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD SPATIAL(' ~ backquote(row['Field']) ~ ');',
|
||||
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_spatial', 'Spatial'|trans) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
@ -215,7 +215,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'ALTER TABLE ' ~ backquote(table) ~ ' ADD FULLTEXT(' ~ backquote(row['Field']) ~ ');',
|
||||
'message_to_show': 'An index has been added on %s.'|trans|format(row['Field']|e)
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_ftext', 'Fulltext'|trans) }}
|
||||
</a>
|
||||
{% else %}
|
||||
@ -234,7 +234,7 @@
|
||||
~ ' GROUP BY ' ~ backquote(row['Field'])
|
||||
~ ' ORDER BY ' ~ backquote(row['Field']),
|
||||
'is_browse_distinct': true
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_browse', 'Distinct values'|trans) }}
|
||||
</a>
|
||||
</li>
|
||||
@ -368,7 +368,8 @@
|
||||
<div id="structure-action-links" class="d-print-none">
|
||||
{% if tbl_is_view and not db_is_system_schema %}
|
||||
{{ link_or_button(
|
||||
url('/view/create', {'db': db, 'table': table}),
|
||||
url('/view/create'),
|
||||
{'db': db, 'table': table},
|
||||
get_icon('b_edit', 'Edit view'|trans, true)
|
||||
) }}
|
||||
{% endif %}
|
||||
@ -381,7 +382,7 @@
|
||||
'table': table,
|
||||
'sql_query': 'SELECT * FROM ' ~ backquote(table) ~ ' PROCEDURE ANALYSE()',
|
||||
'session_max_rows': 'all'
|
||||
}) }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon(
|
||||
'b_tblanalyse',
|
||||
'Propose table structure'|trans,
|
||||
@ -475,7 +476,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'index': index.getName()
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_edit', 'Edit'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -484,7 +485,7 @@
|
||||
'db': db,
|
||||
'table': table,
|
||||
'index': index.getName()
|
||||
}, '') }}">
|
||||
}, '', false) }}">
|
||||
{{ get_icon('b_rename', 'Rename'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -503,7 +504,8 @@
|
||||
|
||||
<input type="hidden" class="drop_primary_key_index_msg" value="{{ index_params.sql_query }}">
|
||||
{{ link_or_button(
|
||||
url('/sql', index_params|merge({'db': db, 'table': table})),
|
||||
url('/sql'),
|
||||
index_params|merge({'db': db, 'table': table}),
|
||||
get_icon('b_drop', 'Drop'|trans),
|
||||
{'class': 'drop_primary_key_index_anchor ajax'}
|
||||
) }}
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
{{- get_common(url_params|merge({
|
||||
'version': version['version'],
|
||||
'submit_delete_version': true
|
||||
}), '') }}">
|
||||
}), '', false) }}">
|
||||
{{ get_icon('b_drop', 'Delete version'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
@ -69,14 +69,14 @@
|
||||
{{- get_common(url_params|merge({
|
||||
'version': version['version'],
|
||||
'report': 'true'
|
||||
}), '') }}">
|
||||
}), '', false) }}">
|
||||
{{ get_icon('b_report', 'Tracking report'|trans) }}
|
||||
</a>
|
||||
<a href="{{ url('/table/tracking') }}" data-post="
|
||||
{{- get_common(url_params|merge({
|
||||
'version': version['version'],
|
||||
'snapshot': 'true'
|
||||
}), '') }}">
|
||||
}), '', false) }}">
|
||||
{{ get_icon('b_props', 'Structure snapshot'|trans) }}
|
||||
</a>
|
||||
</td>
|
||||
|
||||
@ -77,6 +77,7 @@ class ServerTest extends TestCase
|
||||
'tracking_add_drop_view' => true,
|
||||
'tracking_add_drop_table' => true,
|
||||
'tracking_add_drop_database' => true,
|
||||
'hide_connection_errors' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -172,6 +173,7 @@ class ServerTest extends TestCase
|
||||
['tracking_add_drop_view', null, true],
|
||||
['tracking_add_drop_table', null, true],
|
||||
['tracking_add_drop_database', null, true],
|
||||
['hide_connection_errors', null, false],
|
||||
],
|
||||
],
|
||||
'valid values' => [
|
||||
@ -234,6 +236,7 @@ class ServerTest extends TestCase
|
||||
['tracking_add_drop_view', true, true],
|
||||
['tracking_add_drop_table', true, true],
|
||||
['tracking_add_drop_database', true, true],
|
||||
['hide_connection_errors', true, true],
|
||||
],
|
||||
],
|
||||
'valid values 2' => [
|
||||
@ -271,6 +274,7 @@ class ServerTest extends TestCase
|
||||
['tracking_add_drop_view', false, false],
|
||||
['tracking_add_drop_table', false, false],
|
||||
['tracking_add_drop_database', false, false],
|
||||
['hide_connection_errors', false, false],
|
||||
],
|
||||
],
|
||||
'valid values 3' => [
|
||||
@ -346,6 +350,7 @@ class ServerTest extends TestCase
|
||||
['tracking_add_drop_view', 0, false],
|
||||
['tracking_add_drop_table', 0, false],
|
||||
['tracking_add_drop_database', 0, false],
|
||||
['hide_connection_errors', 1, true],
|
||||
],
|
||||
],
|
||||
'invalid values' => [
|
||||
|
||||
@ -78,6 +78,8 @@ class SettingsTest extends TestCase
|
||||
'LoginCookieDeleteAll' => true,
|
||||
'UseDbSearch' => true,
|
||||
'IgnoreMultiSubmitErrors' => false,
|
||||
'URLQueryEncryption' => false,
|
||||
'URLQueryEncryptionSecretKey' => '',
|
||||
'AllowArbitraryServer' => false,
|
||||
'ArbitraryServerRegexp' => '',
|
||||
'CaptchaMethod' => 'invisible',
|
||||
@ -415,6 +417,8 @@ class SettingsTest extends TestCase
|
||||
['LoginCookieDeleteAll', null, true],
|
||||
['UseDbSearch', null, true],
|
||||
['IgnoreMultiSubmitErrors', null, false],
|
||||
['URLQueryEncryption', null, false],
|
||||
['URLQueryEncryptionSecretKey', null, ''],
|
||||
['AllowArbitraryServer', null, false],
|
||||
['ArbitraryServerRegexp', null, ''],
|
||||
['CaptchaMethod', null, 'invisible'],
|
||||
@ -608,6 +612,8 @@ class SettingsTest extends TestCase
|
||||
['LoginCookieDeleteAll', false, false],
|
||||
['UseDbSearch', false, false],
|
||||
['IgnoreMultiSubmitErrors', true, true],
|
||||
['URLQueryEncryption', true, true],
|
||||
['URLQueryEncryptionSecretKey', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'],
|
||||
['AllowArbitraryServer', true, true],
|
||||
['ArbitraryServerRegexp', 'test', 'test'],
|
||||
['CaptchaMethod', 'checkbox', 'checkbox'],
|
||||
@ -927,6 +933,7 @@ class SettingsTest extends TestCase
|
||||
['LoginCookieDeleteAll', 0, false],
|
||||
['UseDbSearch', 0, false],
|
||||
['IgnoreMultiSubmitErrors', 1, true],
|
||||
['URLQueryEncryption', 1, true],
|
||||
['AllowArbitraryServer', 1, true],
|
||||
['ArbitraryServerRegexp', 1234, '1234'],
|
||||
['CaptchaApi', 1234, '1234'],
|
||||
|
||||
@ -5,8 +5,10 @@ declare(strict_types=1);
|
||||
namespace PhpMyAdmin\Tests;
|
||||
|
||||
use PhpMyAdmin\Core;
|
||||
use PhpMyAdmin\Http\ServerRequest;
|
||||
use PhpMyAdmin\ResponseRenderer;
|
||||
use PhpMyAdmin\Sanitize;
|
||||
use PhpMyAdmin\Url;
|
||||
use stdClass;
|
||||
|
||||
use function __;
|
||||
@ -19,6 +21,7 @@ use function ob_get_contents;
|
||||
use function ob_start;
|
||||
use function preg_quote;
|
||||
use function serialize;
|
||||
use function str_repeat;
|
||||
|
||||
/**
|
||||
* @covers \PhpMyAdmin\Core
|
||||
@ -38,6 +41,7 @@ class CoreTest extends AbstractNetworkTestCase
|
||||
$GLOBALS['db'] = '';
|
||||
$GLOBALS['table'] = '';
|
||||
$GLOBALS['PMA_PHP_SELF'] = 'http://example.net/';
|
||||
$GLOBALS['config']->set('URLQueryEncryption', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -924,4 +928,73 @@ class CoreTest extends AbstractNetworkTestCase
|
||||
// Must work now, (good secret and blowfish_secret)
|
||||
$this->assertTrue(Core::checkSqlQuerySignature($sqlQuery, $hmac));
|
||||
}
|
||||
|
||||
public function testPopulateRequestWithEncryptedQueryParams(): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryption', true);
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$_GET = ['pos' => '0', 'eq' => Url::encryptQuery('{"db":"test_db","table":"test_table"}')];
|
||||
$_REQUEST = $_GET;
|
||||
|
||||
$request = $this->createStub(ServerRequest::class);
|
||||
$request->method('getQueryParams')->willReturn($_GET);
|
||||
$request->method('getParsedBody')->willReturn(null);
|
||||
$request->method('withQueryParams')->willReturnSelf();
|
||||
$request->method('withParsedBody')->willReturnSelf();
|
||||
|
||||
Core::populateRequestWithEncryptedQueryParams($request);
|
||||
|
||||
$expected = ['pos' => '0', 'db' => 'test_db', 'table' => 'test_table'];
|
||||
|
||||
$this->assertEquals($expected, $_GET);
|
||||
$this->assertEquals($expected, $_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $encrypted
|
||||
* @param string[] $decrypted
|
||||
*
|
||||
* @dataProvider providerForTestPopulateRequestWithEncryptedQueryParamsWithInvalidParam
|
||||
*/
|
||||
public function testPopulateRequestWithEncryptedQueryParamsWithInvalidParam(
|
||||
array $encrypted,
|
||||
array $decrypted
|
||||
): void {
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryption', true);
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$_GET = $encrypted;
|
||||
$_REQUEST = $encrypted;
|
||||
|
||||
$request = $this->createStub(ServerRequest::class);
|
||||
$request->method('getQueryParams')->willReturn($_GET);
|
||||
$request->method('getParsedBody')->willReturn(null);
|
||||
$request->method('withQueryParams')->willReturnSelf();
|
||||
$request->method('withParsedBody')->willReturnSelf();
|
||||
|
||||
Core::populateRequestWithEncryptedQueryParams($request);
|
||||
|
||||
$this->assertEquals($decrypted, $_GET);
|
||||
$this->assertEquals($decrypted, $_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array<int, array<string, string|mixed[]>>>
|
||||
*/
|
||||
public function providerForTestPopulateRequestWithEncryptedQueryParamsWithInvalidParam(): array
|
||||
{
|
||||
return [
|
||||
[[], []],
|
||||
[['eq' => []], []],
|
||||
[['eq' => ''], []],
|
||||
[['eq' => 'invalid'], []],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
78
test/classes/Crypto/CryptoTest.php
Normal file
78
test/classes/Crypto/CryptoTest.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Crypto;
|
||||
|
||||
use PhpMyAdmin\Crypto\Crypto;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
|
||||
use function mb_strlen;
|
||||
use function str_repeat;
|
||||
|
||||
/**
|
||||
* @covers \PhpMyAdmin\Crypto\Crypto
|
||||
*/
|
||||
class CryptoTest extends AbstractTestCase
|
||||
{
|
||||
public function testWithValidKeyFromConfig(): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$crypto = new Crypto();
|
||||
$encrypted = $crypto->encrypt('test');
|
||||
$this->assertNotSame('test', $encrypted);
|
||||
$this->assertSame('test', $crypto->decrypt($encrypted));
|
||||
$this->assertArrayNotHasKey('URLQueryEncryptionSecretKey', $_SESSION);
|
||||
}
|
||||
|
||||
public function testWithValidKeyFromSession(): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = ['URLQueryEncryptionSecretKey' => str_repeat('a', 32)];
|
||||
$config->set('URLQueryEncryptionSecretKey', '');
|
||||
|
||||
$crypto = new Crypto();
|
||||
$encrypted = $crypto->encrypt('test');
|
||||
$this->assertNotSame('test', $encrypted);
|
||||
$this->assertSame('test', $crypto->decrypt($encrypted));
|
||||
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
|
||||
}
|
||||
|
||||
public function testWithNewSessionKey(): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryptionSecretKey', '');
|
||||
|
||||
$crypto = new Crypto();
|
||||
$encrypted = $crypto->encrypt('test');
|
||||
$this->assertNotSame('test', $encrypted);
|
||||
$this->assertSame('test', $crypto->decrypt($encrypted));
|
||||
$this->assertArrayHasKey('URLQueryEncryptionSecretKey', $_SESSION);
|
||||
$this->assertEquals(32, mb_strlen($_SESSION['URLQueryEncryptionSecretKey'], '8bit'));
|
||||
}
|
||||
|
||||
public function testDecryptWithInvalidKey(): void
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$crypto = new Crypto();
|
||||
$encrypted = $crypto->encrypt('test');
|
||||
$this->assertNotSame('test', $encrypted);
|
||||
$this->assertSame('test', $crypto->decrypt($encrypted));
|
||||
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('b', 32));
|
||||
|
||||
$crypto = new Crypto();
|
||||
$this->assertNull($crypto->decrypt($encrypted));
|
||||
}
|
||||
}
|
||||
@ -1439,11 +1439,12 @@ class ResultsTest extends AbstractTestCase
|
||||
'columns' => [
|
||||
[
|
||||
'column_name' => 'id',
|
||||
'order_link' => '<a href="index.php?route=/sql&db=test_db&table=test_table'
|
||||
'order_link' => '<a href="index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0AORDER+BY+%60id%60+ASC'
|
||||
. '&sql_signature=dcfe20b407b35309f6af81f745e77a10f723d39b082d2a8f9cb8e75b17c4d3ce'
|
||||
. '&session_max_rows=25&is_browse_distinct=0&server=0&lang=en" class="sortlink">id'
|
||||
. '<input type="hidden" value="index.php?route=/sql&db=test_db&table=test_table'
|
||||
. '<input type="hidden" value="'
|
||||
. 'index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0AORDER+BY+%60id%60+ASC'
|
||||
. '&sql_signature=dcfe20b407b35309f6af81f745e77a10f723d39b082d2a8f9cb8e75b17c4d3ce'
|
||||
. '&session_max_rows=25&is_browse_distinct=0&server=0&lang=en"></a>'
|
||||
@ -1465,11 +1466,12 @@ class ResultsTest extends AbstractTestCase
|
||||
],
|
||||
[
|
||||
'column_name' => 'name',
|
||||
'order_link' => '<a href="index.php?route=/sql&db=test_db&table=test_table'
|
||||
'order_link' => '<a href="index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0AORDER+BY+%60name%60+ASC'
|
||||
. '&sql_signature=0d06fa8d6795b1c69892cca27d6213c08401bd434145d16cb35c365ab3e03039'
|
||||
. '&session_max_rows=25&is_browse_distinct=0&server=0&lang=en" class="sortlink">name'
|
||||
. '<input type="hidden" value="index.php?route=/sql&db=test_db&table=test_table'
|
||||
. '<input type="hidden" value="'
|
||||
. 'index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0AORDER+BY+%60name%60+ASC'
|
||||
. '&sql_signature=0d06fa8d6795b1c69892cca27d6213c08401bd434145d16cb35c365ab3e03039'
|
||||
. '&session_max_rows=25&is_browse_distinct=0&server=0&lang=en"></a>'
|
||||
@ -1491,12 +1493,13 @@ class ResultsTest extends AbstractTestCase
|
||||
],
|
||||
[
|
||||
'column_name' => 'datetimefield',
|
||||
'order_link' => '<a href="index.php?route=/sql&db=test_db&table=test_table'
|
||||
'order_link' => '<a href="index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0A'
|
||||
. 'ORDER+BY+%60datetimefield%60+DESC'
|
||||
. '&sql_signature=1c46f7e3c625f9e0846fb2de844ca1732319e5fb7fb93e96c89a4b6218579358'
|
||||
. '&session_max_rows=25&is_browse_distinct=0&server=0&lang=en" class="sortlink">datetimefield'
|
||||
. '<input type="hidden" value="index.php?route=/sql&db=test_db&table=test_table'
|
||||
. '<input type="hidden" value="'
|
||||
. 'index.php?route=/sql&server=0&lang=en&db=test_db&table=test_table'
|
||||
. '&sql_query=SELECT+%2A+FROM+%60test_db%60.%60test_table%60++%0A'
|
||||
. 'ORDER+BY+%60datetimefield%60+DESC'
|
||||
. '&sql_signature=1c46f7e3c625f9e0846fb2de844ca1732319e5fb7fb93e96c89a4b6218579358'
|
||||
|
||||
@ -6,6 +6,7 @@ namespace PhpMyAdmin\Tests\Html;
|
||||
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Tests\AbstractTestCase;
|
||||
use PhpMyAdmin\Url;
|
||||
use PhpMyAdmin\Util;
|
||||
|
||||
use function __;
|
||||
@ -222,10 +223,13 @@ class GeneratorTest extends AbstractTestCase
|
||||
*/
|
||||
public function linksOrButtons(): array
|
||||
{
|
||||
parent::setGlobalConfig();
|
||||
|
||||
return [
|
||||
[
|
||||
[
|
||||
'index.php',
|
||||
null,
|
||||
'text',
|
||||
],
|
||||
1000,
|
||||
@ -233,15 +237,17 @@ class GeneratorTest extends AbstractTestCase
|
||||
],
|
||||
[
|
||||
[
|
||||
'index.php?some=parameter',
|
||||
'index.php',
|
||||
['some' => 'parameter'],
|
||||
'text',
|
||||
],
|
||||
20,
|
||||
'<a href="index.php" data-post="some=parameter">text</a>',
|
||||
'<a href="index.php" data-post="some=parameter&lang=en">text</a>',
|
||||
],
|
||||
[
|
||||
[
|
||||
'index.php',
|
||||
null,
|
||||
'text',
|
||||
[],
|
||||
'target',
|
||||
@ -252,6 +258,7 @@ class GeneratorTest extends AbstractTestCase
|
||||
[
|
||||
[
|
||||
'https://mariadb.org/explain_analyzer/analyze/?client=phpMyAdmin&raw_explain=%2B---%2B',
|
||||
null,
|
||||
'text',
|
||||
[],
|
||||
'target',
|
||||
@ -264,6 +271,7 @@ class GeneratorTest extends AbstractTestCase
|
||||
[
|
||||
[
|
||||
'https://mariadb.org/explain_analyzer/analyze/?client=phpMyAdmin&raw_explain=%2B---%2B',
|
||||
null,
|
||||
'text',
|
||||
[],
|
||||
'target',
|
||||
@ -276,6 +284,7 @@ class GeneratorTest extends AbstractTestCase
|
||||
[
|
||||
[
|
||||
'url.php?url=http://phpmyadmin.net/',
|
||||
null,
|
||||
'text',
|
||||
[],
|
||||
'_blank',
|
||||
@ -283,6 +292,42 @@ class GeneratorTest extends AbstractTestCase
|
||||
1000,
|
||||
'<a href="url.php?url=http://phpmyadmin.net/" target="_blank" rel="noopener noreferrer">text</a>',
|
||||
],
|
||||
[
|
||||
[
|
||||
Url::getFromRoute('/server/databases'),
|
||||
['some' => 'parameter'],
|
||||
'text',
|
||||
],
|
||||
20,
|
||||
'<a href="index.php" data-post="route=/server/databases&some=parameter&lang=en">text</a>',
|
||||
],
|
||||
[
|
||||
[
|
||||
Url::getFromRoute('/server/databases'),
|
||||
null,
|
||||
'text',
|
||||
],
|
||||
20,
|
||||
'<a href="index.php" data-post="route=/server/databases">text</a>',
|
||||
],
|
||||
[
|
||||
[
|
||||
Url::getFromRoute('/server/databases'),
|
||||
['some' => 'parameter'],
|
||||
'text',
|
||||
],
|
||||
100,
|
||||
'<a href="index.php?route=/server/databases&some=parameter&lang=en" >text</a>',
|
||||
],
|
||||
[
|
||||
[
|
||||
Url::getFromRoute('/server/databases'),
|
||||
null,
|
||||
'text',
|
||||
],
|
||||
100,
|
||||
'<a href="index.php?route=/server/databases" >text</a>',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@ namespace PhpMyAdmin\Tests;
|
||||
|
||||
use PhpMyAdmin\Url;
|
||||
|
||||
use function is_string;
|
||||
use function parse_str;
|
||||
use function str_repeat;
|
||||
use function urldecode;
|
||||
|
||||
/**
|
||||
@ -22,6 +25,7 @@ class UrlTest extends AbstractTestCase
|
||||
parent::setUp();
|
||||
parent::setLanguage();
|
||||
unset($_COOKIE['pma_lang']);
|
||||
$GLOBALS['config']->set('URLQueryEncryption', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,4 +181,62 @@ class UrlTest extends AbstractTestCase
|
||||
Url::getHiddenFields([])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testBuildHttpQueryWithUrlQueryEncryptionDisabled()
|
||||
{
|
||||
global $config;
|
||||
|
||||
$config->set('URLQueryEncryption', false);
|
||||
$params = ['db' => 'test_db', 'table' => 'test_table', 'pos' => 0];
|
||||
$this->assertEquals('db=test_db&table=test_table&pos=0', Url::buildHttpQuery($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testBuildHttpQueryWithUrlQueryEncryptionEnabled()
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryption', true);
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$params = ['db' => 'test_db', 'table' => 'test_table', 'pos' => 0];
|
||||
$query = Url::buildHttpQuery($params);
|
||||
$this->assertStringStartsWith('pos=0&eq=', $query);
|
||||
parse_str($query, $queryParams);
|
||||
$this->assertCount(2, $queryParams);
|
||||
$this->assertSame('0', $queryParams['pos']);
|
||||
$this->assertTrue(is_string($queryParams['eq']));
|
||||
$this->assertNotSame('', $queryParams['eq']);
|
||||
$this->assertRegExp('/^[a-zA-Z0-9-_=]+$/', $queryParams['eq']);
|
||||
$decrypted = Url::decryptQuery($queryParams['eq']);
|
||||
$this->assertNotNull($decrypted);
|
||||
$this->assertJson($decrypted);
|
||||
$this->assertSame('{"db":"test_db","table":"test_table"}', $decrypted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testQueryEncryption()
|
||||
{
|
||||
global $config;
|
||||
|
||||
$_SESSION = [];
|
||||
$config->set('URLQueryEncryption', true);
|
||||
$config->set('URLQueryEncryptionSecretKey', str_repeat('a', 32));
|
||||
|
||||
$query = '{"db":"test_db","table":"test_table"}';
|
||||
$encrypted = Url::encryptQuery($query);
|
||||
$this->assertNotSame($query, $encrypted);
|
||||
$this->assertNotSame('', $encrypted);
|
||||
$this->assertRegExp('/^[a-zA-Z0-9-_=]+$/', $encrypted);
|
||||
$decrypted = Url::decryptQuery($encrypted);
|
||||
$this->assertSame($query, $decrypted);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user