2011-01-16 13:51:39 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @group aphront
|
|
|
|
*/
|
|
|
|
class AphrontDefaultApplicationConfiguration
|
|
|
|
extends AphrontApplicationConfiguration {
|
|
|
|
|
2011-02-24 14:52:57 -08:00
|
|
|
public function __construct() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
public function getApplicationName() {
|
|
|
|
return 'aphront-default';
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getURIMap() {
|
2011-02-24 14:52:57 -08:00
|
|
|
return $this->getResourceURIMapRules() + array(
|
2012-10-01 14:05:37 -07:00
|
|
|
'/(?:(?P<filter>(?:jump))/)?' =>
|
2012-02-21 15:10:11 -08:00
|
|
|
'PhabricatorDirectoryMainController',
|
2011-01-24 13:18:41 -08:00
|
|
|
|
2011-01-25 13:48:05 -08:00
|
|
|
'/typeahead/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'common/(?P<type>\w+)/'
|
2011-01-25 13:48:05 -08:00
|
|
|
=> 'PhabricatorTypeaheadCommonDatasourceController',
|
|
|
|
),
|
2011-01-25 17:40:21 -08:00
|
|
|
|
OAuth - Phabricator OAuth server and Phabricator client for new Phabricator OAuth Server
Summary:
adds a Phabricator OAuth server, which has three big commands:
- auth - allows $user to authorize a given client or application. if $user has already authorized, it hands an authoization code back to $redirect_uri
- token - given a valid authorization code, this command returns an authorization token
- whoami - Conduit.whoami, all nice and purdy relative to the oauth server.
Also has a "test" handler, which I used to create some test data. T850 will
delete this as it adds the ability to create this data in the Phabricator
product.
This diff also adds the corresponding client in Phabricator for the Phabricator
OAuth Server. (Note that clients are known as "providers" in the Phabricator
codebase but client makes more sense relative to the server nomenclature)
Also, related to make this work well
- clean up the diagnostics page by variabilizing the provider-specific
information and extending the provider classes as appropriate.
- augment Conduit.whoami for more full-featured OAuth support, at least where
the Phabricator client is concerned
What's missing here... See T844, T848, T849, T850, and T852.
Test Plan:
- created a dummy client via the test handler. setup development.conf to have
have proper variables for this dummy client. went through authorization and
de-authorization flows
- viewed the diagnostics page for all known oauth providers and saw
provider-specific debugging information
Reviewers: epriestley
CC: aran, epriestley
Maniphest Tasks: T44, T797
Differential Revision: https://secure.phabricator.com/D1595
2012-02-03 16:21:40 -08:00
|
|
|
'/oauthserver/' => array(
|
2012-02-22 10:21:39 -08:00
|
|
|
'auth/' => 'PhabricatorOAuthServerAuthController',
|
|
|
|
'test/' => 'PhabricatorOAuthServerTestController',
|
|
|
|
'token/' => 'PhabricatorOAuthServerTokenController',
|
|
|
|
'clientauthorization/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'' => 'PhabricatorOAuthClientAuthorizationListController',
|
2012-02-22 10:21:39 -08:00
|
|
|
'delete/(?P<phid>[^/]+)/' =>
|
|
|
|
'PhabricatorOAuthClientAuthorizationDeleteController',
|
|
|
|
'edit/(?P<phid>[^/]+)/' =>
|
|
|
|
'PhabricatorOAuthClientAuthorizationEditController',
|
|
|
|
),
|
|
|
|
'client/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'' => 'PhabricatorOAuthClientListController',
|
|
|
|
'create/' => 'PhabricatorOAuthClientEditController',
|
|
|
|
'delete/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientDeleteController',
|
|
|
|
'edit/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientEditController',
|
|
|
|
'view/(?P<phid>[^/]+)/' => 'PhabricatorOAuthClientViewController',
|
2012-02-22 10:21:39 -08:00
|
|
|
),
|
OAuth - Phabricator OAuth server and Phabricator client for new Phabricator OAuth Server
Summary:
adds a Phabricator OAuth server, which has three big commands:
- auth - allows $user to authorize a given client or application. if $user has already authorized, it hands an authoization code back to $redirect_uri
- token - given a valid authorization code, this command returns an authorization token
- whoami - Conduit.whoami, all nice and purdy relative to the oauth server.
Also has a "test" handler, which I used to create some test data. T850 will
delete this as it adds the ability to create this data in the Phabricator
product.
This diff also adds the corresponding client in Phabricator for the Phabricator
OAuth Server. (Note that clients are known as "providers" in the Phabricator
codebase but client makes more sense relative to the server nomenclature)
Also, related to make this work well
- clean up the diagnostics page by variabilizing the provider-specific
information and extending the provider classes as appropriate.
- augment Conduit.whoami for more full-featured OAuth support, at least where
the Phabricator client is concerned
What's missing here... See T844, T848, T849, T850, and T852.
Test Plan:
- created a dummy client via the test handler. setup development.conf to have
have proper variables for this dummy client. went through authorization and
de-authorization flows
- viewed the diagnostics page for all known oauth providers and saw
provider-specific debugging information
Reviewers: epriestley
CC: aran, epriestley
Maniphest Tasks: T44, T797
Differential Revision: https://secure.phabricator.com/D1595
2012-02-03 16:21:40 -08:00
|
|
|
),
|
|
|
|
|
DarkConsole: fix rendering, move request log, load over ajax
Summary:
This accomplishes three major goals:
# Fixes phutil_render_tag -> phutil_tag callsites in DarkConsole.
# Moves the Ajax request log to a new panel on the left. This panel (and the tabs panel) get scrollbars when they get large, instead of making the page constantly scroll down.
# Loads the panel content over ajax, instead of dumping it into the page body / ajax response body. I've been planning to do this for about 3 years, which is why the plugins are architected the way they are. This should make debugging easier by making response bodies not be 50%+ darkconsole stuff.
Additionally, load the plugins dynamically (the old method predates library maps and PhutilSymbolLoader).
Test Plan:
{F30675}
- Switched between requests and tabs, reloaded page, saw same tab.
- Used "analyze queries", "profile page", triggered errors.
- Verified page does not load anything by default if dark console is closed with Charles.
- Generally banged on it a bit.
Reviewers: vrana, btrahan, chad
Reviewed By: vrana
CC: aran
Maniphest Tasks: T2432
Differential Revision: https://secure.phabricator.com/D4692
2013-01-28 18:45:32 -08:00
|
|
|
'/~/' => array(
|
|
|
|
'' => 'DarkConsoleController',
|
|
|
|
'data/(?P<key>[^/]+)/' => 'DarkConsoleDataController',
|
|
|
|
),
|
2011-02-05 22:36:21 -08:00
|
|
|
|
2012-03-01 11:46:58 -08:00
|
|
|
'/status/' => 'PhabricatorStatusController',
|
2011-04-08 11:13:29 -07:00
|
|
|
|
2011-06-10 02:53:53 -04:00
|
|
|
|
2011-05-28 11:36:00 -07:00
|
|
|
'/help/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController',
|
2011-05-28 11:36:00 -07:00
|
|
|
),
|
2011-06-12 23:06:17 +00:00
|
|
|
|
2012-06-17 11:35:18 -07:00
|
|
|
'/notification/' => array(
|
2012-06-28 13:59:50 -07:00
|
|
|
'(?:(?P<filter>all|unread)/)?'
|
|
|
|
=> 'PhabricatorNotificationListController',
|
2012-06-17 11:35:18 -07:00
|
|
|
'panel/' => 'PhabricatorNotificationPanelController',
|
|
|
|
'individual/' => 'PhabricatorNotificationIndividualController',
|
|
|
|
'status/' => 'PhabricatorNotificationStatusController',
|
2012-06-20 13:51:19 -07:00
|
|
|
'clear/' => 'PhabricatorNotificationClearController',
|
2012-06-17 11:35:18 -07:00
|
|
|
),
|
|
|
|
|
2013-03-04 13:45:51 -08:00
|
|
|
'/debug/' => 'PhabricatorDebugController',
|
2012-08-05 14:03:39 -07:00
|
|
|
);
|
2011-02-24 14:52:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getResourceURIMapRules() {
|
|
|
|
return array(
|
|
|
|
'/res/' => array(
|
Add timestamps to development-mode static resource URIs
Summary:
When a developer changes CSS, it is normally sufficient to reload the page to get changes to show up, because browsers revalidate resources on reload.
However, if you reload the page and then an Ajax request adds new CSS to the page, this CSS does not trigger revalidation. The developer must currently clear their cache or re-run `scripts/celerity_mapper.php webroot`, to get this request to skip cache. We rarely use CSS over Ajax right now, so this hasn't cropped up much, but Conpherence does use this and clearing the resource is a big pain.
This seems to work fine normally, but I'm worried it might break some of the extra-celerity-resources stuff Facebook is doing.
Test Plan: In development mode, changed `conpherence/message-pane.css` and saw changes reflected on reload. Verified normal page loads do not cause additional HTTP requests. This change has no effect in production mode.
Reviewers: edward, vrana, btrahan
Reviewed By: vrana
CC: aran
Maniphest Tasks: T2428
Differential Revision: https://secure.phabricator.com/D4902
2013-02-11 11:06:41 -08:00
|
|
|
'(?:(?P<mtime>[0-9]+)T/)?'.
|
Use Celerity to version all static resources
Summary:
We don't use versioned URIs for images, so when they change users may get old versions.
This was a particular issue with the recent logo change, which several users reported cache-related issues from.
Instead, use Celerity to manage image URI versions in addition to CSS/JS.
This is complicated, because we need to rewrite image URIs inside of CSS, which means the hash of a CSS file has to be derived from the current image data. Otherwise, when we updated an image the CSS wouldn't update, so we wouldn't be any better off.
So basically we:
- Find all the "raw" files, and put them into the map.
- Find all the CSS/JS, perform content-altering transformations on it (i.e., not minification) based on the partial map, and then put it into the map based on transformed hashes.
(If we wanted, we could now do CSS variables or whatever for "free", more or less.)
Test Plan:
- Regenerated celerity map, browsed site, verified images generated with versioned URIs.
- Moved "blue" flag image over "green" flag image, regenerated map, verified "green" flag image and the associated CSS changed hashes.
- Added transformation unit tests; ran unit tests.
Reviewers: btrahan, vrana, jungejason
Reviewed By: vrana
CC: aran
Maniphest Tasks: T1073
Differential Revision: https://secure.phabricator.com/D2146
2012-04-08 10:07:51 -07:00
|
|
|
'(?P<package>pkg/)?'.
|
|
|
|
'(?P<hash>[a-f0-9]{8})/'.
|
|
|
|
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
|
2012-10-17 08:37:05 -07:00
|
|
|
=> 'CelerityPhabricatorResourceController',
|
2011-02-24 14:52:57 -08:00
|
|
|
),
|
2011-01-16 13:51:39 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-06-24 08:21:42 -07:00
|
|
|
/**
|
|
|
|
* @phutil-external-symbol class PhabricatorStartup
|
|
|
|
*/
|
2011-01-16 13:51:39 -08:00
|
|
|
public function buildRequest() {
|
2013-06-24 08:21:42 -07:00
|
|
|
$parser = new PhutilQueryStringParser();
|
|
|
|
$data = array();
|
|
|
|
|
|
|
|
$raw_input = PhabricatorStartup::getRawInput();
|
|
|
|
if (strlen($raw_input)) {
|
|
|
|
$data += $parser->parseQueryString($raw_input);
|
|
|
|
} else if ($_POST) {
|
|
|
|
$data += $_POST;
|
|
|
|
}
|
|
|
|
|
|
|
|
$data += $parser->parseQueryString(
|
|
|
|
isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : "");
|
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
$request = new AphrontRequest($this->getHost(), $this->getPath());
|
2013-06-24 08:21:42 -07:00
|
|
|
$request->setRequestData($data);
|
2011-02-02 13:48:52 -08:00
|
|
|
$request->setApplicationConfiguration($this);
|
2013-06-24 08:21:42 -07:00
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
return $request;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function handleException(Exception $ex) {
|
2012-05-07 06:17:00 -07:00
|
|
|
$request = $this->getRequest();
|
|
|
|
|
|
|
|
// For Conduit requests, return a Conduit response.
|
|
|
|
if ($request->isConduit()) {
|
|
|
|
$response = new ConduitAPIResponse();
|
|
|
|
$response->setErrorCode(get_class($ex));
|
|
|
|
$response->setErrorInfo($ex->getMessage());
|
|
|
|
|
|
|
|
return id(new AphrontJSONResponse())
|
2013-03-30 19:04:40 -07:00
|
|
|
->setAddJSONShield(false)
|
2012-05-07 06:17:00 -07:00
|
|
|
->setContent($response->toDictionary());
|
|
|
|
}
|
|
|
|
|
|
|
|
// For non-workflow requests, return a Ajax response.
|
|
|
|
if ($request->isAjax() && !$request->isJavelinWorkflow()) {
|
2013-07-15 06:09:37 -07:00
|
|
|
// Log these; they don't get shown on the client and can be difficult
|
|
|
|
// to debug.
|
|
|
|
phlog($ex);
|
|
|
|
|
2012-05-07 06:17:00 -07:00
|
|
|
$response = new AphrontAjaxResponse();
|
|
|
|
$response->setError(
|
|
|
|
array(
|
|
|
|
'code' => get_class($ex),
|
|
|
|
'info' => $ex->getMessage(),
|
|
|
|
));
|
|
|
|
return $response;
|
|
|
|
}
|
2011-01-16 13:51:39 -08:00
|
|
|
|
Add basic per-object privacy policies
Summary:
Provides a basic start for access policies. Objects expose various capabilities, like CAN_VIEW, CAN_EDIT, etc., and set a policy for each capability. We currently implement three policies, PUBLIC (anyone, including logged-out), USERS (any logged-in) and NOONE (nobody). There's also a way to provide automatic capability grants (e.g., the owner of an object can always see it, even if some capability is set to "NOONE"), but I'm not sure how great the implementation feels and it might change.
Most of the code here is providing a primitive for efficient policy-aware list queries. The problem with doing queries naively is that you have to do crazy amounts of filtering, e.g. to show the user page 6, you need to filter at least 600 objects (and likely more) before you can figure out which ones are 500-600 for them. You can't just do "LIMIT 500, 100" because that might have only 50 results, or no results. Instead, the query looks like "WHERE id > last_visible_id", and then we fetch additional pages as necessary to satisfy the request.
The general idea is that we move all data access to Query classes and have them do object filtering. The ID paging primitive allows efficient paging in most cases, and the executeOne() method provides a concise way to do policy checks for edit/view screens.
We'll probably end up with mostly broader policy UIs or configuration-based policies, but there are at least a few cases for per-object privacy (e.g., marking tasks as "Security", and restricting things to the members of projects) so I figured we'd start with a flexible primitive and the simplify it in the UI where we can.
Test Plan: Unit tests, played around in the UI with various policy settings.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D2210
2012-04-14 10:13:29 -07:00
|
|
|
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
2011-01-16 13:51:39 -08:00
|
|
|
|
2012-05-07 06:17:00 -07:00
|
|
|
$user = $request->getUser();
|
2012-04-04 17:53:16 -07:00
|
|
|
if (!$user) {
|
|
|
|
// If we hit an exception very early, we won't have a user.
|
|
|
|
$user = new PhabricatorUser();
|
|
|
|
}
|
|
|
|
|
Add basic per-object privacy policies
Summary:
Provides a basic start for access policies. Objects expose various capabilities, like CAN_VIEW, CAN_EDIT, etc., and set a policy for each capability. We currently implement three policies, PUBLIC (anyone, including logged-out), USERS (any logged-in) and NOONE (nobody). There's also a way to provide automatic capability grants (e.g., the owner of an object can always see it, even if some capability is set to "NOONE"), but I'm not sure how great the implementation feels and it might change.
Most of the code here is providing a primitive for efficient policy-aware list queries. The problem with doing queries naively is that you have to do crazy amounts of filtering, e.g. to show the user page 6, you need to filter at least 600 objects (and likely more) before you can figure out which ones are 500-600 for them. You can't just do "LIMIT 500, 100" because that might have only 50 results, or no results. Instead, the query looks like "WHERE id > last_visible_id", and then we fetch additional pages as necessary to satisfy the request.
The general idea is that we move all data access to Query classes and have them do object filtering. The ID paging primitive allows efficient paging in most cases, and the executeOne() method provides a concise way to do policy checks for edit/view screens.
We'll probably end up with mostly broader policy UIs or configuration-based policies, but there are at least a few cases for per-object privacy (e.g., marking tasks as "Security", and restricting things to the members of projects) so I figured we'd start with a flexible primitive and the simplify it in the UI where we can.
Test Plan: Unit tests, played around in the UI with various policy settings.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D2210
2012-04-14 10:13:29 -07:00
|
|
|
if ($ex instanceof PhabricatorPolicyException) {
|
2012-09-30 19:44:09 -07:00
|
|
|
|
|
|
|
if (!$user->isLoggedIn()) {
|
|
|
|
// If the user isn't logged in, just give them a login form. This is
|
|
|
|
// probably a generally more useful response than a policy dialog that
|
|
|
|
// they have to click through to get a login form.
|
|
|
|
//
|
|
|
|
// Possibly we should add a header here like "you need to login to see
|
|
|
|
// the thing you are trying to look at".
|
2013-06-19 01:33:27 -07:00
|
|
|
$login_controller = new PhabricatorAuthStartController($request);
|
2012-09-30 19:44:09 -07:00
|
|
|
return $login_controller->processRequest();
|
|
|
|
}
|
|
|
|
|
2013-02-08 12:07:44 -08:00
|
|
|
$content = hsprintf(
|
|
|
|
'<div class="aphront-policy-exception">%s</div>',
|
|
|
|
$ex->getMessage());
|
Add basic per-object privacy policies
Summary:
Provides a basic start for access policies. Objects expose various capabilities, like CAN_VIEW, CAN_EDIT, etc., and set a policy for each capability. We currently implement three policies, PUBLIC (anyone, including logged-out), USERS (any logged-in) and NOONE (nobody). There's also a way to provide automatic capability grants (e.g., the owner of an object can always see it, even if some capability is set to "NOONE"), but I'm not sure how great the implementation feels and it might change.
Most of the code here is providing a primitive for efficient policy-aware list queries. The problem with doing queries naively is that you have to do crazy amounts of filtering, e.g. to show the user page 6, you need to filter at least 600 objects (and likely more) before you can figure out which ones are 500-600 for them. You can't just do "LIMIT 500, 100" because that might have only 50 results, or no results. Instead, the query looks like "WHERE id > last_visible_id", and then we fetch additional pages as necessary to satisfy the request.
The general idea is that we move all data access to Query classes and have them do object filtering. The ID paging primitive allows efficient paging in most cases, and the executeOne() method provides a concise way to do policy checks for edit/view screens.
We'll probably end up with mostly broader policy UIs or configuration-based policies, but there are at least a few cases for per-object privacy (e.g., marking tasks as "Security", and restricting things to the members of projects) so I figured we'd start with a flexible primitive and the simplify it in the UI where we can.
Test Plan: Unit tests, played around in the UI with various policy settings.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D2210
2012-04-14 10:13:29 -07:00
|
|
|
|
|
|
|
$dialog = new AphrontDialogView();
|
|
|
|
$dialog
|
|
|
|
->setTitle(
|
|
|
|
$is_serious
|
|
|
|
? 'Access Denied'
|
|
|
|
: "You Shall Not Pass")
|
|
|
|
->setClass('aphront-access-dialog')
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild($content);
|
|
|
|
|
|
|
|
if ($this->getRequest()->isAjax()) {
|
|
|
|
$dialog->addCancelButton('/', 'Close');
|
|
|
|
} else {
|
|
|
|
$dialog->addCancelButton('/', $is_serious ? 'OK' : 'Away With Thee');
|
|
|
|
}
|
|
|
|
|
|
|
|
$response = new AphrontDialogResponse();
|
|
|
|
$response->setDialog($dialog);
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2012-04-23 18:36:25 -07:00
|
|
|
if ($ex instanceof AphrontUsageException) {
|
|
|
|
$error = new AphrontErrorView();
|
2013-02-13 14:50:15 -08:00
|
|
|
$error->setTitle($ex->getTitle());
|
2013-02-06 16:53:49 -08:00
|
|
|
$error->appendChild($ex->getMessage());
|
2012-04-23 18:36:25 -07:00
|
|
|
|
|
|
|
$view = new PhabricatorStandardPageView();
|
|
|
|
$view->setRequest($this->getRequest());
|
|
|
|
$view->appendChild($error);
|
|
|
|
|
|
|
|
$response = new AphrontWebpageResponse();
|
|
|
|
$response->setContent($view->render());
|
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Add basic per-object privacy policies
Summary:
Provides a basic start for access policies. Objects expose various capabilities, like CAN_VIEW, CAN_EDIT, etc., and set a policy for each capability. We currently implement three policies, PUBLIC (anyone, including logged-out), USERS (any logged-in) and NOONE (nobody). There's also a way to provide automatic capability grants (e.g., the owner of an object can always see it, even if some capability is set to "NOONE"), but I'm not sure how great the implementation feels and it might change.
Most of the code here is providing a primitive for efficient policy-aware list queries. The problem with doing queries naively is that you have to do crazy amounts of filtering, e.g. to show the user page 6, you need to filter at least 600 objects (and likely more) before you can figure out which ones are 500-600 for them. You can't just do "LIMIT 500, 100" because that might have only 50 results, or no results. Instead, the query looks like "WHERE id > last_visible_id", and then we fetch additional pages as necessary to satisfy the request.
The general idea is that we move all data access to Query classes and have them do object filtering. The ID paging primitive allows efficient paging in most cases, and the executeOne() method provides a concise way to do policy checks for edit/view screens.
We'll probably end up with mostly broader policy UIs or configuration-based policies, but there are at least a few cases for per-object privacy (e.g., marking tasks as "Security", and restricting things to the members of projects) so I figured we'd start with a flexible primitive and the simplify it in the UI where we can.
Test Plan: Unit tests, played around in the UI with various policy settings.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D2210
2012-04-14 10:13:29 -07:00
|
|
|
// Always log the unhandled exception.
|
|
|
|
phlog($ex);
|
|
|
|
|
2013-02-13 14:50:15 -08:00
|
|
|
$class = get_class($ex);
|
|
|
|
$message = $ex->getMessage();
|
Add basic per-object privacy policies
Summary:
Provides a basic start for access policies. Objects expose various capabilities, like CAN_VIEW, CAN_EDIT, etc., and set a policy for each capability. We currently implement three policies, PUBLIC (anyone, including logged-out), USERS (any logged-in) and NOONE (nobody). There's also a way to provide automatic capability grants (e.g., the owner of an object can always see it, even if some capability is set to "NOONE"), but I'm not sure how great the implementation feels and it might change.
Most of the code here is providing a primitive for efficient policy-aware list queries. The problem with doing queries naively is that you have to do crazy amounts of filtering, e.g. to show the user page 6, you need to filter at least 600 objects (and likely more) before you can figure out which ones are 500-600 for them. You can't just do "LIMIT 500, 100" because that might have only 50 results, or no results. Instead, the query looks like "WHERE id > last_visible_id", and then we fetch additional pages as necessary to satisfy the request.
The general idea is that we move all data access to Query classes and have them do object filtering. The ID paging primitive allows efficient paging in most cases, and the executeOne() method provides a concise way to do policy checks for edit/view screens.
We'll probably end up with mostly broader policy UIs or configuration-based policies, but there are at least a few cases for per-object privacy (e.g., marking tasks as "Security", and restricting things to the members of projects) so I figured we'd start with a flexible primitive and the simplify it in the UI where we can.
Test Plan: Unit tests, played around in the UI with various policy settings.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D2210
2012-04-14 10:13:29 -07:00
|
|
|
|
2012-07-24 10:47:27 -07:00
|
|
|
if ($ex instanceof AphrontQuerySchemaException) {
|
|
|
|
$message .=
|
|
|
|
"\n\n".
|
|
|
|
"NOTE: This usually indicates that the MySQL schema has not been ".
|
|
|
|
"properly upgraded. Run 'bin/storage upgrade' to ensure your ".
|
|
|
|
"schema is up to date.";
|
|
|
|
}
|
|
|
|
|
2013-02-01 09:34:06 -08:00
|
|
|
if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) {
|
2012-04-04 17:53:16 -07:00
|
|
|
$trace = $this->renderStackTrace($ex->getTrace(), $user);
|
2011-08-17 14:29:53 -07:00
|
|
|
} else {
|
|
|
|
$trace = null;
|
|
|
|
}
|
2011-07-07 12:49:59 -07:00
|
|
|
|
2013-02-13 14:50:15 -08:00
|
|
|
$content = hsprintf(
|
2011-01-16 13:51:39 -08:00
|
|
|
'<div class="aphront-unhandled-exception">'.
|
2013-02-13 14:50:15 -08:00
|
|
|
'<div class="exception-message">%s</div>'.
|
|
|
|
'%s'.
|
|
|
|
'</div>',
|
|
|
|
$message,
|
|
|
|
$trace);
|
2011-01-16 13:51:39 -08:00
|
|
|
|
2011-02-26 20:57:21 -08:00
|
|
|
$dialog = new AphrontDialogView();
|
|
|
|
$dialog
|
2011-08-17 14:29:53 -07:00
|
|
|
->setTitle('Unhandled Exception ("'.$class.'")')
|
2011-02-26 20:57:21 -08:00
|
|
|
->setClass('aphront-exception-dialog')
|
|
|
|
->setUser($user)
|
2011-08-17 14:29:53 -07:00
|
|
|
->appendChild($content);
|
|
|
|
|
|
|
|
if ($this->getRequest()->isAjax()) {
|
|
|
|
$dialog->addCancelButton('/', 'Close');
|
|
|
|
}
|
2011-01-16 13:51:39 -08:00
|
|
|
|
2011-02-26 20:57:21 -08:00
|
|
|
$response = new AphrontDialogResponse();
|
|
|
|
$response->setDialog($dialog);
|
2011-01-16 13:51:39 -08:00
|
|
|
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function willSendResponse(AphrontResponse $response) {
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2011-01-29 16:16:09 -08:00
|
|
|
public function build404Controller() {
|
2011-01-30 08:44:28 -08:00
|
|
|
return array(new Phabricator404Controller($this->getRequest()), array());
|
2011-01-29 16:16:09 -08:00
|
|
|
}
|
|
|
|
|
2011-04-04 10:29:46 -07:00
|
|
|
public function buildRedirectController($uri) {
|
|
|
|
return array(
|
|
|
|
new PhabricatorRedirectController($this->getRequest()),
|
|
|
|
array(
|
|
|
|
'uri' => $uri,
|
|
|
|
));
|
|
|
|
}
|
2011-01-16 13:51:39 -08:00
|
|
|
|
2012-04-04 17:53:16 -07:00
|
|
|
private function renderStackTrace($trace, PhabricatorUser $user) {
|
2011-08-17 14:29:53 -07:00
|
|
|
|
|
|
|
$libraries = PhutilBootloader::getInstance()->getAllLibraries();
|
|
|
|
|
|
|
|
// TODO: Make this configurable?
|
2012-04-04 17:53:16 -07:00
|
|
|
$path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
|
|
|
|
|
|
|
|
$callsigns = array(
|
|
|
|
'arcanist' => 'ARC',
|
|
|
|
'phutil' => 'PHU',
|
|
|
|
'phabricator' => 'P',
|
2011-08-17 14:29:53 -07:00
|
|
|
);
|
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
$depth = count($trace);
|
|
|
|
foreach ($trace as $part) {
|
|
|
|
$lib = null;
|
2011-08-21 13:07:09 -07:00
|
|
|
$file = idx($part, 'file');
|
2011-08-17 14:29:53 -07:00
|
|
|
$relative = $file;
|
|
|
|
foreach ($libraries as $library) {
|
|
|
|
$root = phutil_get_library_root($library);
|
|
|
|
if (Filesystem::isDescendant($file, $root)) {
|
|
|
|
$lib = $library;
|
|
|
|
$relative = Filesystem::readablePath($file, $root);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$where = '';
|
|
|
|
if (isset($part['class'])) {
|
|
|
|
$where .= $part['class'].'::';
|
|
|
|
}
|
|
|
|
if (isset($part['function'])) {
|
|
|
|
$where .= $part['function'].'()';
|
|
|
|
}
|
|
|
|
|
2011-08-21 13:07:09 -07:00
|
|
|
if ($file) {
|
2012-04-04 17:53:16 -07:00
|
|
|
if (isset($callsigns[$lib])) {
|
2012-04-07 10:09:19 -07:00
|
|
|
$attrs = array('title' => $file);
|
|
|
|
try {
|
|
|
|
$attrs['href'] = $user->loadEditorLink(
|
2012-04-04 17:53:16 -07:00
|
|
|
'/src/'.$relative,
|
|
|
|
$part['line'],
|
2012-04-07 10:09:19 -07:00
|
|
|
$callsigns[$lib]);
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
// The database can be inaccessible.
|
|
|
|
}
|
|
|
|
if (empty($attrs['href'])) {
|
2012-04-04 17:53:16 -07:00
|
|
|
$attrs['href'] = sprintf($path, $callsigns[$lib]).
|
2012-04-06 15:54:45 -07:00
|
|
|
str_replace(DIRECTORY_SEPARATOR, '/', $relative).
|
|
|
|
'$'.$part['line'];
|
2012-04-04 17:53:16 -07:00
|
|
|
$attrs['target'] = '_blank';
|
|
|
|
}
|
2013-01-17 18:43:35 -08:00
|
|
|
$file_name = phutil_tag(
|
2011-08-21 13:07:09 -07:00
|
|
|
'a',
|
2012-04-04 17:53:16 -07:00
|
|
|
$attrs,
|
2013-01-17 18:43:35 -08:00
|
|
|
$relative);
|
2011-08-21 13:07:09 -07:00
|
|
|
} else {
|
2013-01-17 18:43:35 -08:00
|
|
|
$file_name = phutil_tag(
|
2011-08-21 13:07:09 -07:00
|
|
|
'span',
|
|
|
|
array(
|
|
|
|
'title' => $file,
|
|
|
|
),
|
2013-01-17 18:43:35 -08:00
|
|
|
$relative);
|
2011-08-21 13:07:09 -07:00
|
|
|
}
|
2013-02-13 14:50:15 -08:00
|
|
|
$file_name = hsprintf('%s : %d', $file_name, $part['line']);
|
2011-08-17 14:29:53 -07:00
|
|
|
} else {
|
2013-02-13 14:50:15 -08:00
|
|
|
$file_name = phutil_tag('em', array(), '(Internal)');
|
2011-08-17 14:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$rows[] = array(
|
|
|
|
$depth--,
|
2013-02-13 14:50:15 -08:00
|
|
|
$lib,
|
2011-08-17 14:29:53 -07:00
|
|
|
$file_name,
|
2013-02-13 14:50:15 -08:00
|
|
|
$where,
|
2011-08-17 14:29:53 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
$table = new AphrontTableView($rows);
|
|
|
|
$table->setHeaders(
|
|
|
|
array(
|
|
|
|
'Depth',
|
|
|
|
'Library',
|
|
|
|
'File',
|
|
|
|
'Where',
|
|
|
|
));
|
|
|
|
$table->setColumnClasses(
|
|
|
|
array(
|
|
|
|
'n',
|
|
|
|
'',
|
|
|
|
'',
|
|
|
|
'wide',
|
|
|
|
));
|
|
|
|
|
2013-02-13 14:50:15 -08:00
|
|
|
return hsprintf(
|
2011-08-17 14:29:53 -07:00
|
|
|
'<div class="exception-trace">'.
|
|
|
|
'<div class="exception-trace-header">Stack Trace</div>'.
|
2013-02-15 08:14:31 -08:00
|
|
|
'%s'.
|
2013-02-13 14:50:15 -08:00
|
|
|
'</div>',
|
|
|
|
$table->render());
|
2011-08-17 14:29:53 -07:00
|
|
|
}
|
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
}
|