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';
|
|
|
|
}
|
|
|
|
|
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.
|
Quicksand, an ignoble successor to Quickling
Summary:
Ref T2086. Ref T7014. With the persistent column, there is significant value in retaining chrome state through navigation events, because the user may have a lot of state in the chat window (scroll position, text selection, room juggling, partially entered text, etc). We can do this by capturing navigation events and faking them with Javascript.
(This can also improve performance, albeit slightly, and I believe there are better approaches to tackle performance any problems which exist with the chrome in many cases).
At Facebook, this system was "Photostream" in photos and then "Quickling" in general, and the technical cost of the system was //staggering//. I am loathe to pursue it again. However:
- Browsers are less junky now, and we target a smaller set of browsers. A large part of the technical cost of Quickling was the high complexity of emulating nagivation events in IE, where we needed to navigate a hidden iframe to make history entries. All desktop browsers which we might want to use this system on support the History API (although this prototype does not yet implement it).
- Javelin and Phabricator's architecture are much cleaner than Facebook's was. A large part of the technical cost of Quickling was inconsistency, inlined `onclick` handlers, and general lack of coordination and abstraction. We will have //some// of this, but "correctly written" behaviors are mostly immune to it by design, and many of Javelin's architectural decisions were influenced by desire to avoid issues we encountered building this stuff for Facebook.
- Some of the primitives which Quickling required (like loading resources over Ajax) have existed in a stable state in our codebase for a year or more, and adoption of these primitives was trivial and uneventful (vs a huge production at Facebook).
- My hubris is bolstered by recent success with WebSockets and JX.Scrollbar, both of which I would have assessed as infeasibly complex to develop in this project a few years ago.
To these points, the developer cost to prototype Photostream was several weeks; the developer cost to prototype this was a bit less than an hour. It is plausible to me that implementing and maintaining this system really will be hundreds of times less complex than it was at Facebook.
Test Plan:
My plan for this and D11497 is:
- Get them in master.
- Some secret key / relatively-hidden preference activates the column.
- Quicksand activates //only// when the column is open.
- We can use column + quicksand for a long period of time (i.e., over the course of Conpherence v2 development) and hammer out the long tail of issues.
- When it derps up, you just hide the column and you're good to go.
Reviewers: btrahan, chad
Reviewed By: chad
Subscribers: epriestley
Maniphest Tasks: T2086, T7014
Differential Revision: https://secure.phabricator.com/D11507
2015-01-27 14:52:09 -08:00
|
|
|
if ($request->isAjax() && !$request->isWorkflow()) {
|
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".
|
Decouple some aspects of request routing and construction
Summary:
Ref T5702. This is a forward-looking change which provides some very broad API improvements but does not implement them. In particular:
- Controllers no longer require `$request` to construct. This is mostly for T5702, directly, but simplifies things in general. Instead, we call `setRequest()` before using a controller. Only a small number of sites activate controllers, so this is less code overall, and more consistent with most constructors not having any parameters or effects.
- `$request` now offers `getURIData($key, ...)`. This is an alternate way of accessing `$data` which is currently only available on `willProcessRequest(array $data)`. Almost all controllers which implement this method do so in order to read one or two things out of the URI data. Instead, let them just read this data directly when processing the request.
- Introduce `handleRequest(AphrontRequest $request)` and deprecate (very softly) `processRequest()`. The majority of `processRequest()` calls begin `$request = $this->getRequest()`, which is avoided with the more practical signature.
- Provide `getViewer()` on `$request`, and a convenience `getViewer()` on `$controller`. This fixes `$viewer = $request->getUser();` into `$viewer = $request->getViewer();`, and converts the `$request + $viewer` two-liner into a single `$this->getViewer()`.
Test Plan:
- Browsed around in general.
- Hit special controllers (redirect, 404).
- Hit AuditList controller (uses new style).
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5702
Differential Revision: https://secure.phabricator.com/D10698
2014-10-17 05:01:40 -07:00
|
|
|
$login_controller = new PhabricatorAuthStartController();
|
|
|
|
$login_controller->setRequest($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);
|
|
|
|
|
Decouple some aspects of request routing and construction
Summary:
Ref T5702. This is a forward-looking change which provides some very broad API improvements but does not implement them. In particular:
- Controllers no longer require `$request` to construct. This is mostly for T5702, directly, but simplifies things in general. Instead, we call `setRequest()` before using a controller. Only a small number of sites activate controllers, so this is less code overall, and more consistent with most constructors not having any parameters or effects.
- `$request` now offers `getURIData($key, ...)`. This is an alternate way of accessing `$data` which is currently only available on `willProcessRequest(array $data)`. Almost all controllers which implement this method do so in order to read one or two things out of the URI data. Instead, let them just read this data directly when processing the request.
- Introduce `handleRequest(AphrontRequest $request)` and deprecate (very softly) `processRequest()`. The majority of `processRequest()` calls begin `$request = $this->getRequest()`, which is avoided with the more practical signature.
- Provide `getViewer()` on `$request`, and a convenience `getViewer()` on `$controller`. This fixes `$viewer = $request->getUser();` into `$viewer = $request->getViewer();`, and converts the `$request + $viewer` two-liner into a single `$this->getViewer()`.
Test Plan:
- Browsed around in general.
- Hit special controllers (redirect, 404).
- Hit AuditList controller (uses new style).
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5702
Differential Revision: https://secure.phabricator.com/D10698
2014-10-17 05:01:40 -07:00
|
|
|
return $login_controller->handleRequest($request);
|
2012-09-30 19:44:09 -07:00
|
|
|
}
|
|
|
|
|
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() {
|
Decouple some aspects of request routing and construction
Summary:
Ref T5702. This is a forward-looking change which provides some very broad API improvements but does not implement them. In particular:
- Controllers no longer require `$request` to construct. This is mostly for T5702, directly, but simplifies things in general. Instead, we call `setRequest()` before using a controller. Only a small number of sites activate controllers, so this is less code overall, and more consistent with most constructors not having any parameters or effects.
- `$request` now offers `getURIData($key, ...)`. This is an alternate way of accessing `$data` which is currently only available on `willProcessRequest(array $data)`. Almost all controllers which implement this method do so in order to read one or two things out of the URI data. Instead, let them just read this data directly when processing the request.
- Introduce `handleRequest(AphrontRequest $request)` and deprecate (very softly) `processRequest()`. The majority of `processRequest()` calls begin `$request = $this->getRequest()`, which is avoided with the more practical signature.
- Provide `getViewer()` on `$request`, and a convenience `getViewer()` on `$controller`. This fixes `$viewer = $request->getUser();` into `$viewer = $request->getViewer();`, and converts the `$request + $viewer` two-liner into a single `$this->getViewer()`.
Test Plan:
- Browsed around in general.
- Hit special controllers (redirect, 404).
- Hit AuditList controller (uses new style).
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5702
Differential Revision: https://secure.phabricator.com/D10698
2014-10-17 05:01:40 -07:00
|
|
|
return array(new Phabricator404Controller(), array());
|
2011-01-29 16:16:09 -08:00
|
|
|
}
|
|
|
|
|
2014-08-21 21:35:29 +10:00
|
|
|
public function buildRedirectController($uri, $external) {
|
2011-04-04 10:29:46 -07:00
|
|
|
return array(
|
Decouple some aspects of request routing and construction
Summary:
Ref T5702. This is a forward-looking change which provides some very broad API improvements but does not implement them. In particular:
- Controllers no longer require `$request` to construct. This is mostly for T5702, directly, but simplifies things in general. Instead, we call `setRequest()` before using a controller. Only a small number of sites activate controllers, so this is less code overall, and more consistent with most constructors not having any parameters or effects.
- `$request` now offers `getURIData($key, ...)`. This is an alternate way of accessing `$data` which is currently only available on `willProcessRequest(array $data)`. Almost all controllers which implement this method do so in order to read one or two things out of the URI data. Instead, let them just read this data directly when processing the request.
- Introduce `handleRequest(AphrontRequest $request)` and deprecate (very softly) `processRequest()`. The majority of `processRequest()` calls begin `$request = $this->getRequest()`, which is avoided with the more practical signature.
- Provide `getViewer()` on `$request`, and a convenience `getViewer()` on `$controller`. This fixes `$viewer = $request->getUser();` into `$viewer = $request->getViewer();`, and converts the `$request + $viewer` two-liner into a single `$this->getViewer()`.
Test Plan:
- Browsed around in general.
- Hit special controllers (redirect, 404).
- Hit AuditList controller (uses new style).
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: epriestley
Maniphest Tasks: T5702
Differential Revision: https://secure.phabricator.com/D10698
2014-10-17 05:01:40 -07:00
|
|
|
new PhabricatorRedirectController(),
|
2011-04-04 10:29:46 -07:00
|
|
|
array(
|
|
|
|
'uri' => $uri,
|
2014-08-21 21:35:29 +10:00
|
|
|
'external' => $external,
|
2014-10-08 00:01:04 +11:00
|
|
|
),
|
|
|
|
);
|
2011-04-04 10:29:46 -07:00
|
|
|
}
|
2011-01-16 13:51:39 -08:00
|
|
|
|
|
|
|
}
|