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
|
|
|
|
2011-01-31 11:55:26 -08:00
|
|
|
'/login/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'' => 'PhabricatorLoginController',
|
|
|
|
'email/' => 'PhabricatorEmailLoginController',
|
|
|
|
'etoken/(?P<token>\w+)/' => 'PhabricatorEmailTokenController',
|
|
|
|
'refresh/' => 'PhabricatorRefreshCSRFController',
|
|
|
|
'validate/' => 'PhabricatorLoginValidateController',
|
Allow installs to require email verification
Summary:
Allow installs to require users to verify email addresses before they can use Phabricator. If a user logs in without a verified email address, they're given instructions to verify their address.
This isn't too useful on its own since we don't actually have arbitrary email registration, but the next step is to allow installs to restrict email to only some domains (e.g., @mycompany.com).
Test Plan:
- Verification
- Set verification requirement to `true`.
- Tried to use Phabricator with an unverified account, was told to verify.
- Tried to use Conduit, was given a verification error.
- Verified account, used Phabricator.
- Unverified account, reset password, verified implicit verification, used Phabricator.
- People Admin Interface
- Viewed as admin. Clicked "Administrate User".
- Viewed as non-admin
- Sanity Checks
- Used Conduit normally from web/CLI with a verified account.
- Logged in/out.
- Sent password reset email.
- Created a new user.
- Logged in with an unverified user but with the configuration set to off.
Reviewers: btrahan, vrana, jungejason
Reviewed By: btrahan
CC: aran, csilvers
Maniphest Tasks: T1184
Differential Revision: https://secure.phabricator.com/D2520
2012-05-21 12:47:38 -07:00
|
|
|
'mustverify/' => 'PhabricatorMustVerifyEmailController',
|
2011-01-31 11:55:26 -08:00
|
|
|
),
|
2011-02-24 14:52:57 -08:00
|
|
|
|
2012-03-01 11:46:58 -08:00
|
|
|
'/logout/' => 'PhabricatorLogoutController',
|
2011-02-02 13:48:52 -08:00
|
|
|
|
2011-02-20 22:47:56 -08:00
|
|
|
'/oauth/' => array(
|
Add Google as an OAuth2 provider (BETA)
Summary:
This is pretty straightforward, except:
- We need to request read/write access to the address book to get the account
ID (which we MUST have) and real name, email and account name (which we'd like
to have). This is way more access than we should need, but there's apparently no
"get_loggedin_user_basic_information" type of call in the Google API suite (or,
at least, I couldn't find one).
- We can't get the profile picture or profile URI since there's no Plus API
access and Google users don't have meaningful public pages otherwise.
- Google doesn't save the fact that you've authorized the app, so every time
you want to login you need to reaffirm that you want to give us silly amounts of
access. Phabricator sessions are pretty long-duration though so this shouldn't
be a major issue.
Test Plan:
- Registered, logged out, and logged in with Google.
- Registered, logged out, and logged in with Facebook / Github to make sure I
didn't break anything.
- Linked / unlinked Google accounts.
Reviewers: Makinde, jungejason, nh, tuomaspelkonen, aran
Reviewed By: aran
CC: aran, epriestley, Makinde
Differential Revision: 916
2011-09-08 16:37:22 -07:00
|
|
|
'(?P<provider>\w+)/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'login/' => 'PhabricatorOAuthLoginController',
|
|
|
|
'diagnose/' => 'PhabricatorOAuthDiagnosticsController',
|
|
|
|
'unlink/' => 'PhabricatorOAuthUnlinkController',
|
2011-02-20 22:47:56 -08:00
|
|
|
),
|
|
|
|
),
|
|
|
|
|
2012-06-13 08:52:05 -07:00
|
|
|
'/ldap/' => array(
|
|
|
|
'login/' => 'PhabricatorLDAPLoginController',
|
|
|
|
'unlink/' => 'PhabricatorLDAPUnlinkController',
|
|
|
|
),
|
|
|
|
|
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
|
|
|
),
|
|
|
|
|
2011-02-02 13:48:52 -08:00
|
|
|
'/xhprof/' => array(
|
2012-08-24 15:14:38 -07:00
|
|
|
'list/(?P<view>[^/]+)/' => 'PhabricatorXHProfSampleListController',
|
2012-03-01 11:46:58 -08:00
|
|
|
'profile/(?P<phid>[^/]+)/' => 'PhabricatorXHProfProfileController',
|
2011-02-02 13:48:52 -08:00
|
|
|
),
|
2011-02-02 22:38:42 -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
|
|
|
|
2011-02-14 15:34:20 -08:00
|
|
|
'/search/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'' => 'PhabricatorSearchController',
|
|
|
|
'(?P<key>[^/]+)/' => 'PhabricatorSearchController',
|
|
|
|
'attach/(?P<phid>[^/]+)/(?P<type>\w+)/(?:(?P<action>\w+)/)?'
|
2011-05-16 11:43:39 -07:00
|
|
|
=> 'PhabricatorSearchAttachController',
|
2012-03-01 11:46:58 -08:00
|
|
|
'select/(?P<type>\w+)/'
|
2011-05-16 11:43:39 -07:00
|
|
|
=> 'PhabricatorSearchSelectController',
|
2012-03-01 11:46:58 -08:00
|
|
|
'index/(?P<phid>[^/]+)/' => 'PhabricatorSearchIndexController',
|
2011-02-14 15:34:20 -08:00
|
|
|
),
|
2011-02-20 18:41:23 -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-02-17 10:21:38 -08:00
|
|
|
'/chatlog/' => array(
|
2012-03-01 11:46:58 -08:00
|
|
|
'' =>
|
2012-02-17 10:21:38 -08:00
|
|
|
'PhabricatorChatLogChannelListController',
|
2012-03-01 11:46:58 -08:00
|
|
|
'channel/(?P<channel>[^/]+)/' =>
|
2012-02-17 10:21:38 -08:00
|
|
|
'PhabricatorChatLogChannelLogController',
|
|
|
|
),
|
2012-03-06 20:14:03 -08: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
|
|
|
),
|
|
|
|
|
2012-04-04 16:09:29 -07:00
|
|
|
'/phortune/' => array(
|
|
|
|
'stripe/' => array(
|
|
|
|
'testpaymentform/' => 'PhortuneStripeTestPaymentFormController',
|
|
|
|
),
|
|
|
|
),
|
2012-08-05 14:03:39 -07:00
|
|
|
);
|
2011-02-24 14:52:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getResourceURIMapRules() {
|
|
|
|
return array(
|
|
|
|
'/res/' => array(
|
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
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildRequest() {
|
|
|
|
$request = new AphrontRequest($this->getHost(), $this->getPath());
|
|
|
|
$request->setRequestData($_GET + $_POST);
|
2011-02-02 13:48:52 -08:00
|
|
|
$request->setApplicationConfiguration($this);
|
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())
|
|
|
|
->setContent($response->toDictionary());
|
|
|
|
}
|
|
|
|
|
|
|
|
// For non-workflow requests, return a Ajax response.
|
|
|
|
if ($request->isAjax() && !$request->isJavelinWorkflow()) {
|
|
|
|
$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".
|
|
|
|
$login_controller = new PhabricatorLoginController($request);
|
|
|
|
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-09 09:23:02 -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-09 09:23:02 -08:00
|
|
|
$class = get_class($ex);
|
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
|
|
|
$message = phutil_escape_html($ex->getMessage());
|
|
|
|
|
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
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
$content =
|
|
|
|
'<div class="aphront-unhandled-exception">'.
|
2011-08-17 14:29:53 -07:00
|
|
|
'<div class="exception-message">'.$message.'</div>'.
|
|
|
|
$trace.
|
2011-01-16 13:51:39 -08:00
|
|
|
'</div>';
|
|
|
|
|
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
|
|
|
}
|
|
|
|
$file_name = $file_name.' : '.(int)$part['line'];
|
2011-08-17 14:29:53 -07:00
|
|
|
} else {
|
2011-08-21 13:07:09 -07:00
|
|
|
$file_name = '<em>(Internal)</em>';
|
2011-08-17 14:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$rows[] = array(
|
|
|
|
$depth--,
|
|
|
|
phutil_escape_html($lib),
|
|
|
|
$file_name,
|
|
|
|
phutil_escape_html($where),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$table = new AphrontTableView($rows);
|
|
|
|
$table->setHeaders(
|
|
|
|
array(
|
|
|
|
'Depth',
|
|
|
|
'Library',
|
|
|
|
'File',
|
|
|
|
'Where',
|
|
|
|
));
|
|
|
|
$table->setColumnClasses(
|
|
|
|
array(
|
|
|
|
'n',
|
|
|
|
'',
|
|
|
|
'',
|
|
|
|
'wide',
|
|
|
|
));
|
|
|
|
|
|
|
|
return
|
|
|
|
'<div class="exception-trace">'.
|
|
|
|
'<div class="exception-trace-header">Stack Trace</div>'.
|
|
|
|
$table->render().
|
|
|
|
'</div>';
|
|
|
|
}
|
|
|
|
|
2011-01-16 13:51:39 -08:00
|
|
|
}
|