2011-01-16 13:51:39 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
2014-02-26 13:01:45 -08:00
|
|
|
* NOTE: Do not extend this!
|
|
|
|
*
|
|
|
|
* @concrete-extensible
|
2011-01-16 13:51:39 -08:00
|
|
|
*/
|
|
|
|
class AphrontDefaultApplicationConfiguration
|
|
|
|
extends AphrontApplicationConfiguration {
|
|
|
|
|
2014-07-23 10:03:09 +10:00
|
|
|
public function __construct() {}
|
2011-02-24 14:52:57 -08:00
|
|
|
|
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(
|
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',
|
|
|
|
),
|
2012-08-05 14:03:39 -07:00
|
|
|
);
|
2011-02-24 14:52:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getResourceURIMapRules() {
|
Serve .eot and .ttf through Celerity
Summary:
D9153 fixed half of this, but exposed another issue, which is that we don't actually serve ".eot" and ".ttf" through Celerity right now.
Make sure we include them in the routes.
Test Plan:
- Downloaded CSS, JS, TTF, EOT, WOFF, JPG, etc., through Celerity.
Reviewers: btrahan, chad
Reviewed By: chad
Subscribers: epriestley
Differential Revision: https://secure.phabricator.com/D9154
2014-05-16 09:53:18 -07:00
|
|
|
$extensions = CelerityResourceController::getSupportedResourceTypes();
|
|
|
|
$extensions = array_keys($extensions);
|
|
|
|
$extensions = implode('|', $extensions);
|
|
|
|
|
2011-02-24 14:52:57 -08:00
|
|
|
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/)?'.
|
2014-01-02 11:59:45 -08:00
|
|
|
'(?P<library>[^/]+)/'.
|
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<hash>[a-f0-9]{8})/'.
|
Serve .eot and .ttf through Celerity
Summary:
D9153 fixed half of this, but exposed another issue, which is that we don't actually serve ".eot" and ".ttf" through Celerity right now.
Make sure we include them in the routes.
Test Plan:
- Downloaded CSS, JS, TTF, EOT, WOFF, JPG, etc., through Celerity.
Reviewers: btrahan, chad
Reviewed By: chad
Subscribers: epriestley
Differential Revision: https://secure.phabricator.com/D9154
2014-05-16 09:53:18 -07:00
|
|
|
'(?P<path>.+\.(?:'.$extensions.'))'
|
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();
|
|
|
|
|
2013-08-04 11:37:17 -07:00
|
|
|
// If the request has "multipart/form-data" content, we can't use
|
|
|
|
// PhutilQueryStringParser to parse it, and the raw data supposedly is not
|
|
|
|
// available anyway (according to the PHP documentation, "php://input" is
|
|
|
|
// not available for "multipart/form-data" requests). However, it is
|
|
|
|
// available at least some of the time (see T3673), so double check that
|
|
|
|
// we aren't trying to parse data we won't be able to parse correctly by
|
|
|
|
// examining the Content-Type header.
|
|
|
|
$content_type = idx($_SERVER, 'CONTENT_TYPE');
|
|
|
|
$is_form_data = preg_match('@^multipart/form-data@i', $content_type);
|
|
|
|
|
2013-06-24 08:21:42 -07:00
|
|
|
$raw_input = PhabricatorStartup::getRawInput();
|
2013-08-04 11:37:17 -07:00
|
|
|
if (strlen($raw_input) && !$is_form_data) {
|
2013-06-24 08:21:42 -07:00
|
|
|
$data += $parser->parseQueryString($raw_input);
|
|
|
|
} else if ($_POST) {
|
|
|
|
$data += $_POST;
|
|
|
|
}
|
|
|
|
|
2013-08-04 11:37:17 -07:00
|
|
|
$data += $parser->parseQueryString(idx($_SERVER, 'QUERY_STRING', ''));
|
2013-06-24 08:21:42 -07:00
|
|
|
|
2014-01-17 08:07:57 -08:00
|
|
|
$cookie_prefix = PhabricatorEnv::getEnvConfig('phabricator.cookie-prefix');
|
|
|
|
|
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);
|
2014-01-17 08:07:57 -08:00
|
|
|
$request->setCookiePrefix($cookie_prefix);
|
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
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2014-04-03 11:22:38 -07:00
|
|
|
if ($ex instanceof PhabricatorSystemActionRateLimitException) {
|
|
|
|
$dialog = id(new AphrontDialogView())
|
|
|
|
->setTitle(pht('Slow Down!'))
|
|
|
|
->setUser($user)
|
2014-04-03 11:23:03 -07:00
|
|
|
->setErrors(array(pht('You are being rate limited.')))
|
2014-04-03 11:22:38 -07:00
|
|
|
->appendParagraph($ex->getMessage())
|
|
|
|
->appendParagraph($ex->getRateExplanation())
|
|
|
|
->addCancelButton('/', pht('Okaaaaaaaaaaaaaay...'));
|
|
|
|
|
|
|
|
$response = new AphrontDialogResponse();
|
2014-04-27 17:31:11 -07:00
|
|
|
$response->setDialog($dialog);
|
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($ex instanceof PhabricatorAuthHighSecurityRequiredException) {
|
|
|
|
|
|
|
|
$form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm(
|
2014-04-28 10:20:54 -07:00
|
|
|
$ex->getFactors(),
|
|
|
|
$ex->getFactorValidationResults(),
|
2014-04-27 17:31:11 -07:00
|
|
|
$user,
|
|
|
|
$request);
|
|
|
|
|
|
|
|
$dialog = id(new AphrontDialogView())
|
|
|
|
->setUser($user)
|
|
|
|
->setTitle(pht('Entering High Security'))
|
|
|
|
->setShortTitle(pht('Security Checkpoint'))
|
|
|
|
->setWidth(AphrontDialogView::WIDTH_FORM)
|
|
|
|
->addHiddenInput(AphrontRequest::TYPE_HISEC, true)
|
|
|
|
->setErrors(
|
|
|
|
array(
|
|
|
|
pht(
|
|
|
|
'You are taking an action which requires you to enter '.
|
|
|
|
'high security.'),
|
|
|
|
))
|
|
|
|
->appendParagraph(
|
|
|
|
pht(
|
|
|
|
'High security mode helps protect your account from security '.
|
|
|
|
'threats, like session theft or someone messing with your stuff '.
|
|
|
|
'while you\'re grabbing a coffee. To enter high security mode, '.
|
|
|
|
'confirm your credentials.'))
|
|
|
|
->appendChild($form->buildLayoutView())
|
|
|
|
->appendParagraph(
|
|
|
|
pht(
|
|
|
|
'Your account will remain in high security mode for a short '.
|
|
|
|
'period of time. When you are finished taking sensitive '.
|
|
|
|
'actions, you should leave high security.'))
|
|
|
|
->setSubmitURI($request->getPath())
|
|
|
|
->addCancelButton($ex->getCancelURI())
|
|
|
|
->addSubmitButton(pht('Enter High Security'));
|
|
|
|
|
|
|
|
foreach ($request->getPassthroughRequestParameters() as $key => $value) {
|
|
|
|
$dialog->addHiddenInput($key, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
$response = new AphrontDialogResponse();
|
2014-04-03 11:22:38 -07:00
|
|
|
$response->setDialog($dialog);
|
|
|
|
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
|
|
|
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);
|
2013-10-14 11:46:26 -07:00
|
|
|
|
2014-07-23 10:03:09 +10:00
|
|
|
$auth_app_class = 'PhabricatorAuthApplication';
|
2013-10-14 11:46:26 -07:00
|
|
|
$auth_app = PhabricatorApplication::getByClass($auth_app_class);
|
|
|
|
$login_controller->setCurrentApplication($auth_app);
|
|
|
|
|
2012-09-30 19:44:09 -07:00
|
|
|
return $login_controller->processRequest();
|
|
|
|
}
|
|
|
|
|
2013-09-27 08:43:50 -07:00
|
|
|
$list = $ex->getMoreInfo();
|
|
|
|
foreach ($list as $key => $item) {
|
|
|
|
$list[$key] = phutil_tag('li', array(), $item);
|
|
|
|
}
|
|
|
|
if ($list) {
|
|
|
|
$list = phutil_tag('ul', array(), $list);
|
|
|
|
}
|
|
|
|
|
Allow applications to define new policy capabilities
Summary:
Ref T603. I want to let applications define new capabilities (like "can manage global rules" in Herald) and get full support for them, including reasonable error strings in the UI.
Currently, this is difficult for a couple of reasons. Partly this is just a code organization issue, which is easy to fix. The bigger thing is that we have a bunch of strings which depend on both the policy and capability, like: "You must be an administrator to view this object." "Administrator" is the policy, and "view" is the capability.
That means every new capability has to add a string for each policy, and every new policy (should we introduce any) needs to add a string for each capability. And we can't do any piecemeal "You must be a {$role} to {$action} this object" becuase it's impossible to translate.
Instead, make all the strings depend on //only// the policy, //only// the capability, or //only// the object type. This makes the dialogs read a little more strangely, but I think it's still pretty easy to understand, and it makes adding new stuff way way easier.
Also provide more context, and more useful exception messages.
Test Plan:
- See screenshots.
- Also triggered a policy exception and verified it was dramatically more useful than it used to be.
Reviewers: btrahan, chad
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7260
2013-10-07 13:28:58 -07:00
|
|
|
$content = array(
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'aphront-policy-rejection',
|
|
|
|
),
|
|
|
|
$ex->getRejection()),
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'aphront-capability-details',
|
|
|
|
),
|
|
|
|
pht('Users with the "%s" capability:', $ex->getCapabilityName())),
|
|
|
|
$list,
|
|
|
|
);
|
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
|
Allow applications to define new policy capabilities
Summary:
Ref T603. I want to let applications define new capabilities (like "can manage global rules" in Herald) and get full support for them, including reasonable error strings in the UI.
Currently, this is difficult for a couple of reasons. Partly this is just a code organization issue, which is easy to fix. The bigger thing is that we have a bunch of strings which depend on both the policy and capability, like: "You must be an administrator to view this object." "Administrator" is the policy, and "view" is the capability.
That means every new capability has to add a string for each policy, and every new policy (should we introduce any) needs to add a string for each capability. And we can't do any piecemeal "You must be a {$role} to {$action} this object" becuase it's impossible to translate.
Instead, make all the strings depend on //only// the policy, //only// the capability, or //only// the object type. This makes the dialogs read a little more strangely, but I think it's still pretty easy to understand, and it makes adding new stuff way way easier.
Also provide more context, and more useful exception messages.
Test Plan:
- See screenshots.
- Also triggered a policy exception and verified it was dramatically more useful than it used to be.
Reviewers: btrahan, chad
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T603
Differential Revision: https://secure.phabricator.com/D7260
2013-10-07 13:28:58 -07:00
|
|
|
->setTitle($ex->getTitle())
|
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
|
|
|
->setClass('aphront-access-dialog')
|
|
|
|
->setUser($user)
|
|
|
|
->appendChild($content);
|
|
|
|
|
|
|
|
if ($this->getRequest()->isAjax()) {
|
2014-04-18 17:51:46 -07:00
|
|
|
$dialog->addCancelButton('/', pht('Close'));
|
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
|
|
|
} else {
|
2014-04-18 17:51:46 -07:00
|
|
|
$dialog->addCancelButton('/', pht('OK'));
|
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
|
|
|
}
|
|
|
|
|
|
|
|
$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());
|
2013-11-05 15:25:12 -08:00
|
|
|
$response->setHTTPResponseCode(500);
|
2012-04-23 18:36:25 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
2014-08-06 07:51:21 +10:00
|
|
|
if ($ex instanceof AphrontSchemaQueryException) {
|
2012-07-24 10:47:27 -07:00
|
|
|
$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')) {
|
2013-11-21 14:38:29 -08:00
|
|
|
$trace = id(new AphrontStackTraceView())
|
|
|
|
->setUser($user)
|
|
|
|
->setTrace($ex->getTrace());
|
2011-08-17 14:29:53 -07:00
|
|
|
} else {
|
|
|
|
$trace = null;
|
|
|
|
}
|
2011-07-07 12:49:59 -07:00
|
|
|
|
2013-11-08 20:44:24 -08:00
|
|
|
$content = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array('class' => 'aphront-unhandled-exception'),
|
|
|
|
array(
|
|
|
|
phutil_tag('div', array('class' => 'exception-message'), $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);
|
2013-11-05 15:25:12 -08:00
|
|
|
$response->setHTTPResponseCode(500);
|
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
|
|
|
|
|
|
|
}
|