Skip to content
Permalink
Browse files
Add admin SQL queries page to allow admin to easily query the databas…
…e and store queries for later reference.

Improved UI/UX of members list page in admin area.
Fix various low severity stored and reflected xss vulnerabilities.
Fix CSRF issues in various admin pages.
Code refactoring in various files.
  • Loading branch information
Ahmad Gneady committed Jul 3, 2021
1 parent 0a2aaf5 commit 2dc485ed006650c2c0d06467cb9e26f88a782481
Showing with 1,040 additions and 229 deletions.
  1. +26 −0 app/admin/ajax-saved-sql.php
  2. +34 −0 app/admin/ajax-sql.php
  3. +2 −1 app/admin/getUsers.php
  4. +40 −1 app/admin/incFunctions.php
  5. +1 −0 app/admin/incHeader.php
  6. +2 −0 app/admin/pageDeleteGroup.php
  7. +8 −8 app/admin/pageDeleteMember.php
  8. +4 −2 app/admin/pageDeleteRecord.php
  9. +24 −0 app/admin/pageDeleteRecord.php.bak
  10. +1 −1 app/admin/pageEditMember.php
  11. +1 −1 app/admin/pageEditMemberPermissions.php
  12. +1 −1 app/admin/pageMail.php
  13. +177 −0 app/admin/pageQueryLogs.php
  14. +277 −0 app/admin/pageSQL.js
  15. +124 −0 app/admin/pageSQL.php
  16. +2 −2 app/admin/pageServerStatus.php
  17. +1 −1 app/admin/pageTransferOwnership.php
  18. +2 −2 app/admin/pageViewGroups.php
  19. +160 −72 app/admin/pageViewMembers.php
  20. +1 −1 app/admin/pageViewRecords.php
  21. +1 −1 app/ajax_admin_tools.php
  22. +1 −1 app/ajax_combo.php
  23. +1 −1 app/applicants_and_tenants_autofill.php
  24. +2 −2 app/applicants_and_tenants_dml.php
  25. +1 −1 app/applicants_and_tenants_view.php
  26. +1 −1 app/applications_leases_autofill.php
  27. +2 −2 app/applications_leases_dml.php
  28. +1 −1 app/applications_leases_view.php
  29. +13 −0 app/defaultLang.php
  30. +1 −1 app/dynamic.css
  31. +1 −1 app/employment_and_income_history_autofill.php
  32. +2 −2 app/employment_and_income_history_dml.php
  33. +1 −1 app/employment_and_income_history_view.php
  34. +1 −1 app/footer.php
  35. +1 −11 app/incCommon.php
  36. +53 −40 app/language.php
  37. +1 −1 app/lib.php
  38. +5 −4 app/login.php
  39. +8 −9 app/membership_passwordReset.php
  40. +2 −2 app/membership_profile.php
  41. +1 −1 app/parent-children.php
  42. +1 −1 app/properties_autofill.php
  43. +1 −1 app/properties_dml.php
  44. +1 −1 app/properties_view.php
  45. +1 −1 app/property_photos_autofill.php
  46. +2 −2 app/property_photos_dml.php
  47. +1 −1 app/property_photos_view.php
  48. +1 −1 app/references_autofill.php
  49. +1 −1 app/references_dml.php
  50. +1 −1 app/references_view.php
  51. +1 −1 app/rental_owners_autofill.php
  52. +2 −2 app/rental_owners_dml.php
  53. +1 −1 app/rental_owners_view.php
  54. +1 −1 app/residence_and_rental_history_autofill.php
  55. +2 −2 app/residence_and_rental_history_dml.php
  56. +1 −1 app/residence_and_rental_history_view.php
  57. +2 −2 app/templates/applicants_and_tenants-ajax-cache.php
  58. +2 −2 app/templates/applications_leases-ajax-cache.php
  59. +2 −2 app/templates/employment_and_income_history-ajax-cache.php
  60. +2 −2 app/templates/properties-ajax-cache.php
  61. +2 −2 app/templates/property_photos-ajax-cache.php
  62. +2 −2 app/templates/references-ajax-cache.php
  63. +2 −2 app/templates/rental_owners-ajax-cache.php
  64. +2 −2 app/templates/residence_and_rental_history-ajax-cache.php
  65. +2 −2 app/templates/unit_photos-ajax-cache.php
  66. +2 −2 app/templates/units-ajax-cache.php
  67. +1 −1 app/unit_photos_autofill.php
  68. +2 −2 app/unit_photos_dml.php
  69. +1 −1 app/unit_photos_view.php
  70. +6 −6 app/units_autofill.php
  71. +2 −2 app/units_dml.php
  72. +1 −1 app/units_view.php
  73. +1 −1 orpm.axp
@@ -0,0 +1,26 @@
<?php
/*
Manage stored SQL queries for admin user.
Parameters:
queries: (optional) a json string [{name, query}, ..]) to store.
Response:
stored queries (as a json string).
queries are stored in the membership_users.data field for the current user, under the key 'storedQueries'
*/

$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");

if(!csrf_token(true)) {
@header('HTTP/1.0 403 Access Denied');
die();
}

// store queries if provided
if(isset($_REQUEST['queries'])) {
$queries = $_REQUEST['queries'];
setUserData('storedQueries', $queries);
}

echo getUserData('storedQueries');
@@ -0,0 +1,34 @@
<?php
$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");

if(!csrf_token(true)) {
@header('HTTP/1.0 403 Access Denied');
die();
}

$sql = trim($_REQUEST['sql']);
if(!preg_match('/^SELECT\s+.*?\s+FROM\s+\S+/i', $sql)) {
@header('HTTP/1.0 404 Not Found');
die("Invalid query");
}

// force a limit of 1000 in case no limit specified
if(!preg_match('/\s+limit\s+\d+(\s*,\s*\d+)?/i', $sql))
$sql .= ' LIMIT 1000';

$resp = ['titles' => [], 'data' => [], 'error' => ''];
$eo = ['silentErrors' => true];

$res = sql($sql, $eo);
if(!$res)
$resp['error'] = $eo['error'];
else while($row = db_fetch_assoc($res)) {
if(!count($resp['titles']))
$resp['titles'] = array_keys($row);

$resp['data'][] = array_map('htmlspecialchars', array_values($row));
}

@header('Content-type: application/json');
echo json_encode($resp, JSON_PARTIAL_OUTPUT_ON_ERROR);
@@ -1,5 +1,5 @@
<?php
// This script and data application were generated by AppGini 5.97
// This script and data application were generated by AppGini 6.0
// Download AppGini for free from https://bigprof.com/appgini/download/

/*
@@ -56,6 +56,7 @@
}
$res = sql("select u.memberID, g.name from membership_users u left join membership_groups g on u.groupID=g.groupID left join membership_grouppermissions p on g.groupID=p.groupID and p.tableName='{$table_name}' where {$where} order by g.name, u.memberID limit {$skip}, {$results_per_page}", $eo);
while($row = db_fetch_row($res)) {
$row = array_map('strip_tags', $row);
$prepared_data[] = array('id' => to_utf8($row[0]), 'text' => to_utf8("<b>{$row[1]}</b>/{$row[0]}"));
}

@@ -1216,7 +1216,9 @@ function update_membership_users() {
`comments` TEXT,
`pass_reset_key` VARCHAR(100),
`pass_reset_expiry` INT UNSIGNED,
`flags` TEXT,
`allowCSVImport` TINYINT NOT NULL DEFAULT '0',
`data` LONGTEXT,
PRIMARY KEY (`memberID`),
INDEX `groupID` (`groupID`)
) CHARSET " . mysql_charset,
@@ -1229,6 +1231,7 @@ function update_membership_users() {
sql("ALTER TABLE `{$tn}` ADD INDEX `groupID` (`groupID`)", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `flags` TEXT", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `allowCSVImport` TINYINT NOT NULL DEFAULT '0'", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `data` LONGTEXT", $eo);
}
########################################################################
function update_membership_userrecords() {
@@ -1582,7 +1585,7 @@ function html_attr($str) {
function html_attr_tags_ok($str) {
// use this instead of html_attr() if you don't want html tags to be escaped
$new_str = html_attr($str);
return str_replace(array('&lt;', '&gt;'), array('<', '>'), $new_str);
return str_replace(['&lt;', '&gt;'], ['<', '>'], $new_str);
}
#########################################################
class Notification{
@@ -2582,3 +2585,39 @@ function assocArrFilter($arr, $func) {

return $filtered;
}
#########################################################
function setUserData($key, $value = null) {
$data = [];

$user = makeSafe(getMemberInfo()['username']);
if(!$user) return false;

$dataJson = sqlValue("SELECT `data` FROM `membership_users` WHERE `memberID`='$user'");
if($dataJson) {
$data = @json_decode($dataJson, true);
if(!$data) $data = [];
}

$data[$key] = $value;

return update(
'membership_users',
['data' => @json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR)],
['memberID' => $user]
);
}
#########################################################
function getUserData($key) {
$user = makeSafe(getMemberInfo()['username']);
if(!$user) return null;

$dataJson = sqlValue("SELECT `data` FROM `membership_users` WHERE `memberID`='$user'");
if(!$dataJson) return null;

$data = @json_decode($dataJson, true);
if(!$data) return null;

if(!isset($data[$key])) return null;

return $data[$key];
}
@@ -213,6 +213,7 @@ function hideDialogs() {
<li><a href="pageBackupRestore.php"><i class="glyphicon menu-item-icon text-info glyphicon-tasks"></i> <?php echo $Translation['database backups']; ?></a></li>
<li><a href="pageUploadCSV.php"><i class="glyphicon menu-item-icon text-info glyphicon-upload"></i> <?php echo $Translation['import CSV']; ?></a></li>
<li><a href="pageQueryLogs.php"><i class="glyphicon menu-item-icon text-info glyphicon-book"></i> <?php echo $Translation['Query logs']; ?></a></li>
<li><a href="pageSQL.php"><i class="glyphicon menu-item-icon text-danger glyphicon-console"></i> <?php echo $Translation['Interactive SQL queries tool']; ?></a></li>
<li class="divider"></li>
<li><a href="https://forums.appgini.com" target="_blank"><i class="glyphicon menu-item-icon text-info glyphicon-new-window"></i> <?php echo $Translation['AppGini forum']; ?></a></li>
</ul>
@@ -5,6 +5,8 @@
// validate input
$groupID = intval($_GET['groupID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

// make sure group has no members
if(sqlValue("select count(1) from membership_users where groupID='{$groupID}'")) {
errorMsg($Translation["can not delete group remove members"]);
@@ -1,17 +1,17 @@
<?php
$currDir=dirname(__FILE__);
require("$currDir/incCommon.php");
require(__DIR__ . '/incCommon.php');

// validate input
$memberID=makeSafe(strtolower($_GET['memberID']));
$memberID = makeSafe(strtolower($_GET['memberID']));

sql("delete from membership_users where lcase(memberID)='$memberID'", $eo);
sql("update membership_userrecords set memberID='' where lcase(memberID)='$memberID'", $eo);
if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
sql("DELETE FROM `membership_users` WHERE LCASE(`memberID`)='$memberID'", $eo);
sql("UPDATE `membership_userrecords` SET `memberID`='' WHERE LCASE(`memberID`)='$memberID'", $eo);

if($_SERVER['HTTP_REFERER']) {
redirect($_SERVER['HTTP_REFERER'], TRUE);
} else {
redirect("admin/pageViewMembers.php");
redirect('admin/pageViewMembers.php');
}

?>
@@ -1,10 +1,12 @@
<?php
$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");
require(__DIR__ . '/incCommon.php');

// validate input
$recID = intval($_GET['recID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
$res = sql("SELECT `tableName`, `pkValue` FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($row = db_fetch_row($res)) {
sql("DELETE FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
@@ -0,0 +1,24 @@
<?php

require(__DIR__ . '/incCommon.php');

// validate input
$recID = intval($_GET['recID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
$res = sql("SELECT `tableName`, `pkValue` FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($row = db_fetch_row($res)) {
sql("DELETE FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($pkName = getPKFieldName($row[0])) {
sql("DELETE FROM `{$row[0]}` WHERE `{$pkName}`='" . makeSafe($row[1]) . "'", $eo);
}
}

if($_SERVER['HTTP_REFERER']) {
redirect($_SERVER['HTTP_REFERER'], TRUE);
exit;
}

redirect('admin/pageViewRecords.php');
@@ -139,7 +139,7 @@
} elseif($_GET['groupID'] != '') {
// show the form for adding a new member, and pre-select the provided group
$groupID = intval($_GET['groupID']);
$group_name = sqlValue("select name from membership_groups where groupID='$groupID'");
$group_name = strip_tags(sqlValue("select name from membership_groups where groupID='$groupID'"));
if($group_name)
$addend = " to '{$group_name}'";
}
@@ -19,7 +19,7 @@
$anonGroupID = sqlValue("select groupID from membership_groups where lcase(name)='" . strtolower(makeSafe($anonymousGroup)) . "'");
$adminGroupID = sqlValue("select groupID from membership_groups where name='Admins'");
$groupID = sqlValue("select groupID from membership_users where lcase(memberID)='{$memberID->sql}'");
$group = sqlValue("select name from membership_groups where groupID='{$groupID}'");
$group = strip_tags(sqlValue("select name from membership_groups where groupID='{$groupID}'"));
if($groupID == $anonGroupID || $memberID->raw == $anonymousMember || !$groupID || $groupID == $adminGroupID || $memberID->raw == $adminConfig['adminUsername']) {
// error in request. redirect to members page.
redirect('admin/pageViewMembers.php');
@@ -139,7 +139,7 @@
<label class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["from"]; ?></label>
<div class="col-sm-8 col-md-9 col-lg-6">
<p class="form-control-static">
<?php echo "{$adminConfig['senderName']} &lt;{$adminConfig['senderEmail']}&gt;"; ?>
<?php echo strip_tags($adminConfig['senderName']) . " &lt;{$adminConfig['senderEmail']}&gt;"; ?>
<div>
<a href="pageSettings.php?search-settings=smtp" class="btn btn-default">
<i class="glyphicon glyphicon-pencil"></i>

0 comments on commit 2dc485e

Please sign in to comment.