@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make `bin/storage upgrade` and `bin/storage adjust` emit detailed messages if the user has no access to databases

Summary:
Ref T10195. Distinguish between "database does not exist" and "database exists, you just don't have permission to access it".

We can't easily get this information out of INFORMATION_SCHEMA but can just `SHOW TABLES IN ...` every database that looks like it's missing and then look at the error code.

Test Plan:
- Created a user `limited` with limited access.
- Ran `bin/storage adjust`.
- Got hopefully more helpful messages about access problems, instead of "Missing" errors.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10195

Differential Revision: https://secure.phabricator.com/D15079

+94 -6
+19 -5
src/applications/config/schema/PhabricatorConfigDatabaseSchema.php
··· 6 6 private $characterSet; 7 7 private $collation; 8 8 private $tables = array(); 9 + private $accessDenied; 9 10 10 11 public function addTable(PhabricatorConfigTableSchema $table) { 11 12 $key = $table->getName(); ··· 33 34 PhabricatorConfigStorageSchema $expect) { 34 35 35 36 $issues = array(); 36 - if ($this->getCharacterSet() != $expect->getCharacterSet()) { 37 - $issues[] = self::ISSUE_CHARSET; 38 - } 37 + if ($this->getAccessDenied()) { 38 + $issues[] = self::ISSUE_ACCESSDENIED; 39 + } else { 40 + if ($this->getCharacterSet() != $expect->getCharacterSet()) { 41 + $issues[] = self::ISSUE_CHARSET; 42 + } 39 43 40 - if ($this->getCollation() != $expect->getCollation()) { 41 - $issues[] = self::ISSUE_COLLATION; 44 + if ($this->getCollation() != $expect->getCollation()) { 45 + $issues[] = self::ISSUE_COLLATION; 46 + } 42 47 } 43 48 44 49 return $issues; ··· 66 71 67 72 public function getCharacterSet() { 68 73 return $this->characterSet; 74 + } 75 + 76 + public function setAccessDenied($access_denied) { 77 + $this->accessDenied = $access_denied; 78 + return $this; 79 + } 80 + 81 + public function getAccessDenied() { 82 + return $this->accessDenied; 69 83 } 70 84 71 85 }
+27
src/applications/config/schema/PhabricatorConfigSchemaQuery.php
··· 47 47 $databases); 48 48 $database_info = ipull($database_info, null, 'SCHEMA_NAME'); 49 49 50 + // Find databases which exist, but which the user does not have permission 51 + // to see. 52 + $invisible_databases = array(); 53 + foreach ($databases as $database_name) { 54 + if (isset($database_info[$database_name])) { 55 + continue; 56 + } 57 + 58 + try { 59 + queryfx($conn, 'SHOW TABLES IN %T', $database_name); 60 + } catch (AphrontAccessDeniedQueryException $ex) { 61 + // This database exists, the user just doesn't have permission to 62 + // see it. 63 + $invisible_databases[] = $database_name; 64 + } catch (AphrontSchemaQueryException $ex) { 65 + // This database is legitimately missing. 66 + } 67 + } 68 + 50 69 $sql = array(); 51 70 foreach ($tables as $table) { 52 71 $sql[] = qsprintf( ··· 148 167 $server_schema->addDatabase($database_schema); 149 168 } 150 169 170 + foreach ($invisible_databases as $database_name) { 171 + $server_schema->addDatabase( 172 + id(new PhabricatorConfigDatabaseSchema()) 173 + ->setName($database_name) 174 + ->setAccessDenied(true)); 175 + } 176 + 151 177 return $server_schema; 152 178 } 153 179 ··· 194 220 if (!$actual_database) { 195 221 $actual_database = $expect_database->newEmptyClone(); 196 222 } 223 + 197 224 if (!$expect_database) { 198 225 $expect_database = $actual_database->newEmptyClone(); 199 226 }
+4
src/applications/config/schema/PhabricatorConfigStorageSchema.php
··· 17 17 const ISSUE_SUBFAIL = 'subfail'; 18 18 const ISSUE_AUTOINCREMENT = 'autoincrement'; 19 19 const ISSUE_UNKNOWN = 'unknown'; 20 + const ISSUE_ACCESSDENIED = 'accessdenied'; 20 21 21 22 const STATUS_OKAY = 'okay'; 22 23 const STATUS_WARN = 'warn'; ··· 130 131 return pht('Column has Wrong Autoincrement'); 131 132 case self::ISSUE_UNKNOWN: 132 133 return pht('Column Has No Specification'); 134 + case self::ISSUE_ACCESSDENIED: 135 + return pht('Access Denied'); 133 136 default: 134 137 throw new Exception(pht('Unknown schema issue "%s"!', $issue)); 135 138 } ··· 175 178 public static function getIssueStatus($issue) { 176 179 switch ($issue) { 177 180 case self::ISSUE_MISSING: 181 + case self::ISSUE_ACCESSDENIED: 178 182 case self::ISSUE_SURPLUS: 179 183 case self::ISSUE_NULLABLE: 180 184 case self::ISSUE_SUBFAIL:
+10
src/infrastructure/storage/management/PhabricatorStorageManagementAPI.php
··· 123 123 'SELECT patch FROM %T', 124 124 self::TABLE_STATUS); 125 125 return ipull($applied, 'patch'); 126 + } catch (AphrontAccessDeniedQueryException $ex) { 127 + throw new PhutilProxyException( 128 + pht( 129 + 'Failed while trying to read schema status: the database "%s" '. 130 + 'exists, but the current user ("%s") does not have permission to '. 131 + 'access it. GRANT the current user more permissions, or use a '. 132 + 'different user.', 133 + $this->getDatabaseName('meta_data'), 134 + $this->getUser()), 135 + $ex); 126 136 } catch (AphrontQueryException $ex) { 127 137 return null; 128 138 }
+34 -1
src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php
··· 422 422 continue; 423 423 } 424 424 425 + if ($actual_database->getAccessDenied()) { 426 + // If we can't access the database, we can't access the tables either. 427 + continue; 428 + } 429 + 425 430 $issues = array(); 426 431 if ($database->hasIssue($issue_charset)) { 427 432 $issues[] = $issue_charset; ··· 634 639 635 640 $any_surplus = false; 636 641 $all_surplus = true; 642 + $any_access = false; 643 + $all_access = true; 637 644 foreach ($errors as $error) { 638 645 $pieces = array_select_keys( 639 646 $error, ··· 643 650 644 651 $name = PhabricatorConfigStorageSchema::getIssueName($error['issue']); 645 652 646 - if ($error['issue'] === PhabricatorConfigStorageSchema::ISSUE_SURPLUS) { 653 + $issue = $error['issue']; 654 + 655 + if ($issue === PhabricatorConfigStorageSchema::ISSUE_SURPLUS) { 647 656 $any_surplus = true; 648 657 } else { 649 658 $all_surplus = false; 650 659 } 651 660 661 + if ($issue === PhabricatorConfigStorageSchema::ISSUE_ACCESSDENIED) { 662 + $any_access = true; 663 + } else { 664 + $all_access = false; 665 + } 666 + 652 667 $table->addRow( 653 668 array( 654 669 'target' => $target, ··· 668 683 'does not expect). For information on resolving these '. 669 684 'issues, see the "Surplus Schemata" section in the "Managing Storage '. 670 685 'Adjustments" article in the documentation.'); 686 + } else if ($all_access) { 687 + $message[] = pht( 688 + 'The user you are connecting to MySQL with does not have the correct '. 689 + 'permissions, and can not access some databases or tables that it '. 690 + 'needs to be able to access. GRANT the user additional permissions.'); 671 691 } else { 672 692 $message[] = pht( 673 693 'The schemata have errors (detailed above) which the adjustment '. 674 694 'workflow can not fix.'); 695 + 696 + if ($any_access) { 697 + $message[] = pht( 698 + 'Some of these errors are caused by access control problems. '. 699 + 'The user you are connecting with does not have permission to see '. 700 + 'all of the database or tables that Phabricator uses. You need to '. 701 + 'GRANT the user more permission, or use a different user.'); 702 + } 675 703 676 704 if ($any_surplus) { 677 705 $message[] = pht( ··· 697 725 $console->writeOut( 698 726 "**<bg:yellow> %s </bg>**\n\n%s\n", 699 727 pht('SURPLUS SCHEMATA'), 728 + phutil_console_wrap($message)); 729 + } else if ($all_access) { 730 + $console->writeOut( 731 + "**<bg:yellow> %s </bg>**\n\n%s\n", 732 + pht('ACCESS DENIED'), 700 733 phutil_console_wrap($message)); 701 734 } else { 702 735 $console->writeOut(