2011-01-16 22:51:39 +01:00
|
|
|
<?php
|
|
|
|
|
2011-01-23 02:48:55 +01:00
|
|
|
abstract class PhabricatorController extends AphrontController {
|
2011-01-16 22:51:39 +01:00
|
|
|
|
2012-08-15 19:45:06 +02:00
|
|
|
private $handles;
|
|
|
|
|
2011-01-26 22:21:12 +01:00
|
|
|
public function shouldRequireLogin() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-05-12 19:06:54 +02:00
|
|
|
public function shouldRequireAdmin() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function shouldRequireEnabledUser() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-10-01 04:44:09 +02:00
|
|
|
public function shouldAllowPublic() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-05-01 19:23:02 +02:00
|
|
|
public function shouldAllowPartialSessions() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
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 21:47:38 +02:00
|
|
|
public function shouldRequireEmailVerification() {
|
2013-10-04 04:05:47 +02:00
|
|
|
return PhabricatorUserEmail::isEmailVerificationRequired();
|
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 21:47:38 +02:00
|
|
|
}
|
|
|
|
|
Whitelist controllers which can receive a 'code' parameter
Summary:
Ref T4593. There are a variety of clever attacks against OAuth which involve changing the redirect URI to some other URI on the same domain which exhibits unexpected behavior in response to an OAuth request. The best approach to dealing with this is for providers to lock to a specific path and refuse to redirect elsewhere, but not all providers do this.
We haven't had any specific issues related to this, but the anchor issue in T4593 was only a step away.
To mitigate this in general, we can reject the OAuth2 `'code'` parameter on //every// page by default, and then whitelist it on the tiny number of controllers which should be able to receive it.
This is very coarse, kind of overkill, and has some fallout (we can't use `'code'` as a normal parameter in the application), but I think it's relatively well-contained and seems reasonable. A better approach might be to whitelist parameters on every controller (i.e., have each controller specify the parameters it can receive), but that would be a ton of work and probably cause a lot of false positives for a long time.
Since we don't use `'code'` normally anywhere (as far as I can tell), the coarseness of this approach seems reasonable.
Test Plan:
- Logged in with OAuth.
- Hit any other page with `?code=...` in the URL, got an exception.
- Grepped for `'code'` and `"code"`, and examined each use to see if it was impacted.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: aran, epriestley
Maniphest Tasks: T4593
Differential Revision: https://secure.phabricator.com/D8499
2014-03-12 19:30:04 +01:00
|
|
|
public function shouldAllowRestrictedParameter($parameter_name) {
|
|
|
|
return false;
|
|
|
|
}
|
2011-01-26 22:21:12 +01:00
|
|
|
|
2014-06-04 01:50:27 +02:00
|
|
|
public function shouldRequireMultiFactorEnrollment() {
|
|
|
|
if (!$this->shouldRequireLogin()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$this->shouldRequireEnabledUser()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->shouldAllowPartialSessions()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$user = $this->getRequest()->getUser();
|
|
|
|
if (!$user->getIsStandardUser()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PhabricatorEnv::getEnvConfig('security.require-multi-factor-auth');
|
|
|
|
}
|
|
|
|
|
2015-02-13 00:22:56 +01:00
|
|
|
public function shouldAllowLegallyNonCompliantUsers() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-04-24 00:08:35 +02:00
|
|
|
public function isGlobalDragAndDropUploadEnabled() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Whitelist controllers which can receive a 'code' parameter
Summary:
Ref T4593. There are a variety of clever attacks against OAuth which involve changing the redirect URI to some other URI on the same domain which exhibits unexpected behavior in response to an OAuth request. The best approach to dealing with this is for providers to lock to a specific path and refuse to redirect elsewhere, but not all providers do this.
We haven't had any specific issues related to this, but the anchor issue in T4593 was only a step away.
To mitigate this in general, we can reject the OAuth2 `'code'` parameter on //every// page by default, and then whitelist it on the tiny number of controllers which should be able to receive it.
This is very coarse, kind of overkill, and has some fallout (we can't use `'code'` as a normal parameter in the application), but I think it's relatively well-contained and seems reasonable. A better approach might be to whitelist parameters on every controller (i.e., have each controller specify the parameters it can receive), but that would be a ton of work and probably cause a lot of false positives for a long time.
Since we don't use `'code'` normally anywhere (as far as I can tell), the coarseness of this approach seems reasonable.
Test Plan:
- Logged in with OAuth.
- Hit any other page with `?code=...` in the URL, got an exception.
- Grepped for `'code'` and `"code"`, and examined each use to see if it was impacted.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: aran, epriestley
Maniphest Tasks: T4593
Differential Revision: https://secure.phabricator.com/D8499
2014-03-12 19:30:04 +01:00
|
|
|
public function willBeginExecution() {
|
2011-01-26 22:21:12 +01:00
|
|
|
$request = $this->getRequest();
|
Whitelist controllers which can receive a 'code' parameter
Summary:
Ref T4593. There are a variety of clever attacks against OAuth which involve changing the redirect URI to some other URI on the same domain which exhibits unexpected behavior in response to an OAuth request. The best approach to dealing with this is for providers to lock to a specific path and refuse to redirect elsewhere, but not all providers do this.
We haven't had any specific issues related to this, but the anchor issue in T4593 was only a step away.
To mitigate this in general, we can reject the OAuth2 `'code'` parameter on //every// page by default, and then whitelist it on the tiny number of controllers which should be able to receive it.
This is very coarse, kind of overkill, and has some fallout (we can't use `'code'` as a normal parameter in the application), but I think it's relatively well-contained and seems reasonable. A better approach might be to whitelist parameters on every controller (i.e., have each controller specify the parameters it can receive), but that would be a ton of work and probably cause a lot of false positives for a long time.
Since we don't use `'code'` normally anywhere (as far as I can tell), the coarseness of this approach seems reasonable.
Test Plan:
- Logged in with OAuth.
- Hit any other page with `?code=...` in the URL, got an exception.
- Grepped for `'code'` and `"code"`, and examined each use to see if it was impacted.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: aran, epriestley
Maniphest Tasks: T4593
Differential Revision: https://secure.phabricator.com/D8499
2014-03-12 19:30:04 +01:00
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
if ($request->getUser()) {
|
|
|
|
// NOTE: Unit tests can set a user explicitly. Normal requests are not
|
|
|
|
// permitted to do this.
|
|
|
|
PhabricatorTestCase::assertExecutingUnitTests();
|
|
|
|
$user = $request->getUser();
|
|
|
|
} else {
|
|
|
|
$user = new PhabricatorUser();
|
2014-01-23 23:03:54 +01:00
|
|
|
$session_engine = new PhabricatorAuthSessionEngine();
|
2013-10-04 04:05:47 +02:00
|
|
|
|
2014-01-23 23:01:35 +01:00
|
|
|
$phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION);
|
Issue "anonymous" sessions for logged-out users
Summary:
Ref T4339. Ref T4310. Currently, sessions look like `"afad85d675fda87a4fadd54"`, and are only issued for logged-in users. To support logged-out CSRF and (eventually) external user sessions, I made two small changes:
- First, sessions now have a "kind", which is indicated by a prefix, like `"A/ab987asdcas7dca"`. This mostly allows us to issue session queries more efficiently: we don't have to issue a query at all for anonymous sessions, and can join the correct table for user and external sessions and save a query. Generally, this gives us more debugging information and more opportunity to recover from issues in a user-friendly way, as with the "invalid session" error in this diff.
- Secondly, if you load a page and don't have a session, we give you an anonymous session. This is just a secret with no special significance.
This does not implement CSRF yet, but gives us a client secret we can use to implement it.
Test Plan:
- Logged in.
- Logged out.
- Browsed around.
- Logged in again.
- Went through link/register.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T4310, T4339
Differential Revision: https://secure.phabricator.com/D8043
2014-01-23 23:03:22 +01:00
|
|
|
if (strlen($phsid)) {
|
2014-01-23 23:03:54 +01:00
|
|
|
$session_user = $session_engine->loadUserForSession(
|
|
|
|
PhabricatorAuthSession::TYPE_WEB,
|
|
|
|
$phsid);
|
2014-01-14 22:22:27 +01:00
|
|
|
if ($session_user) {
|
|
|
|
$user = $session_user;
|
2013-10-04 04:05:47 +02:00
|
|
|
}
|
2014-01-23 23:03:54 +01:00
|
|
|
} else {
|
|
|
|
// If the client doesn't have a session token, generate an anonymous
|
|
|
|
// session. This is used to provide CSRF protection to logged-out users.
|
|
|
|
$phsid = $session_engine->establishSession(
|
|
|
|
PhabricatorAuthSession::TYPE_WEB,
|
2014-05-01 19:23:02 +02:00
|
|
|
null,
|
|
|
|
$partial = false);
|
Don't try to set anonymous session cookie on CDN/file domain
Summary:
Ref T2380. If an install has a CDN domain configured, but does not list it as an alternate domain (which is standard/correct, but not incredibly common, see T2380), we'll currently try to set anonymous cookies on it. These will correctly fail security rules.
Instead, don't try to set these cookies.
I missed this in testing yesterday because I have a file domain, but I also have it configured as an alternate domain, which allows cookies to be set. Generally, domain management is due for some refactoring.
Test Plan: Set file domain but not as an alternate, logged out, nuked file domain cookies, reloaded page. No error after patch.
Reviewers: btrahan, csilvers
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2380
Differential Revision: https://secure.phabricator.com/D8057
2014-01-24 21:29:03 +01:00
|
|
|
|
|
|
|
// This may be a resource request, in which case we just don't set
|
|
|
|
// the cookie.
|
|
|
|
if ($request->canSetCookies()) {
|
|
|
|
$request->setCookie(PhabricatorCookies::COOKIE_SESSION, $phsid);
|
|
|
|
}
|
2014-01-23 23:03:54 +01:00
|
|
|
}
|
|
|
|
|
Don't try to set anonymous session cookie on CDN/file domain
Summary:
Ref T2380. If an install has a CDN domain configured, but does not list it as an alternate domain (which is standard/correct, but not incredibly common, see T2380), we'll currently try to set anonymous cookies on it. These will correctly fail security rules.
Instead, don't try to set these cookies.
I missed this in testing yesterday because I have a file domain, but I also have it configured as an alternate domain, which allows cookies to be set. Generally, domain management is due for some refactoring.
Test Plan: Set file domain but not as an alternate, logged out, nuked file domain cookies, reloaded page. No error after patch.
Reviewers: btrahan, csilvers
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2380
Differential Revision: https://secure.phabricator.com/D8057
2014-01-24 21:29:03 +01:00
|
|
|
|
2014-01-23 23:03:54 +01:00
|
|
|
if (!$user->isLoggedIn()) {
|
Remove "phabricator.csrf-key" and upgrade CSRF hashing to SHA256
Summary:
Ref T12509.
- Remove the "phabricator.csrf-key" configuration option in favor of automatically generating an HMAC key.
- Upgrade two hasher callsites (one in CSRF itself, one in providing a CSRF secret for logged-out users) to SHA256.
- Extract the CSRF logic from `PhabricatorUser` to a standalone engine.
I was originally going to do this as two changes (extract logic, then upgrade hashes) but the logic had a couple of very silly pieces to it that made faithful extraction a little silly.
For example, it computed `time_block = (epoch + (offset * cycle_frequency)) / cycle_frequency` instead of `time_block = (epoch / cycle_frequency) + offset`. These are equivalent but the former was kind of silly.
It also computed `substr(hmac(substr(hmac(secret)).salt))` instead of `substr(hmac(secret.salt))`. These have the same overall effect but the former is, again, kind of silly (and a little bit materially worse, in this case).
This will cause a one-time compatibility break: pages loaded before the upgrade won't be able to submit contained forms after the upgrade, unless they're open for long enough for the Javascript to refresh the CSRF token (an hour, I think?). I'll note this in the changelog.
Test Plan:
- As a logged-in user, submitted forms normally (worked).
- As a logged-in user, submitted forms with a bad CSRF value (error, as expected).
- As a logged-out user, hit the success and error cases.
- Visually inspected tokens for correct format.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T12509
Differential Revision: https://secure.phabricator.com/D19946
2019-01-03 14:55:19 +01:00
|
|
|
$csrf = PhabricatorHash::digestWithNamedKey($phsid, 'csrf.alternate');
|
|
|
|
$user->attachAlternateCSRFString($csrf);
|
2011-01-26 22:21:12 +01:00
|
|
|
}
|
2013-10-04 04:05:47 +02:00
|
|
|
|
|
|
|
$request->setUser($user);
|
2011-01-26 22:21:12 +01:00
|
|
|
}
|
|
|
|
|
2016-06-07 15:52:56 +02:00
|
|
|
id(new PhabricatorAuthSessionEngine())
|
|
|
|
->willServeRequestForUser($user);
|
2012-06-15 03:08:06 +02:00
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {
|
Provide a general-purpose, modular user cache for settings and other similar data
Summary:
Ref T4103. Currently, we issue a `SELECT * FROM user_preferences ... WHERE userPHID = ...` on every page to load the viewer's settings.
There are several other questionable data accesses on every page too, most of which could benefit from improved caching strategies (see T4103#178122).
This query will soon get more expensive, since it may need to load several objects (e.g., the user's settings and their "role profile" settings). Although we could put that data on the User and do both in one query, it's nicer to put it on the Preferences object ("This inherits from profile X") which means we need to do several queries.
Rather than paying a greater price, we can cheat this stuff into the existing query where we load the user's session by providing a user cache table and doing some JOIN magic. This lets us issue one query and try to get cache hits on a bunch of caches cheaply (well, we'll be in trouble at the MySQL JOIN limit of 61 tables, but have some headroom).
For now, just get it working:
- Add the table.
- Try to get user settings "for free" when we load the session.
- If we miss, fill user settings into the cache on-demand.
- We only use this in one place (DarkConsole) for now. I'll use it more widely in the next diff.
Test Plan:
- Loaded page as logged-in user.
- Loaded page as logged-out user.
- Examined session query to see cache joins.
- Changed settings, saw database cache fill.
- Toggled DarkConsole on and off.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4103
Differential Revision: https://secure.phabricator.com/D16001
2016-06-01 21:14:39 +02:00
|
|
|
$dark_console = PhabricatorDarkConsoleSetting::SETTINGKEY;
|
|
|
|
if ($user->getUserSetting($dark_console) ||
|
2013-10-04 04:05:47 +02:00
|
|
|
PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
|
|
|
|
$console = new DarkConsoleCore();
|
|
|
|
$request->getApplicationConfiguration()->setConsole($console);
|
|
|
|
}
|
|
|
|
}
|
2011-02-05 21:20:18 +01:00
|
|
|
|
Whitelist controllers which can receive a 'code' parameter
Summary:
Ref T4593. There are a variety of clever attacks against OAuth which involve changing the redirect URI to some other URI on the same domain which exhibits unexpected behavior in response to an OAuth request. The best approach to dealing with this is for providers to lock to a specific path and refuse to redirect elsewhere, but not all providers do this.
We haven't had any specific issues related to this, but the anchor issue in T4593 was only a step away.
To mitigate this in general, we can reject the OAuth2 `'code'` parameter on //every// page by default, and then whitelist it on the tiny number of controllers which should be able to receive it.
This is very coarse, kind of overkill, and has some fallout (we can't use `'code'` as a normal parameter in the application), but I think it's relatively well-contained and seems reasonable. A better approach might be to whitelist parameters on every controller (i.e., have each controller specify the parameters it can receive), but that would be a ton of work and probably cause a lot of false positives for a long time.
Since we don't use `'code'` normally anywhere (as far as I can tell), the coarseness of this approach seems reasonable.
Test Plan:
- Logged in with OAuth.
- Hit any other page with `?code=...` in the URL, got an exception.
- Grepped for `'code'` and `"code"`, and examined each use to see if it was impacted.
Reviewers: btrahan
Reviewed By: btrahan
Subscribers: aran, epriestley
Maniphest Tasks: T4593
Differential Revision: https://secure.phabricator.com/D8499
2014-03-12 19:30:04 +01:00
|
|
|
// NOTE: We want to set up the user first so we can render a real page
|
|
|
|
// here, but fire this before any real logic.
|
|
|
|
$restricted = array(
|
|
|
|
'code',
|
|
|
|
);
|
|
|
|
foreach ($restricted as $parameter) {
|
|
|
|
if ($request->getExists($parameter)) {
|
|
|
|
if (!$this->shouldAllowRestrictedParameter($parameter)) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Request includes restricted parameter "%s", but this '.
|
|
|
|
'controller ("%s") does not whitelist it. Refusing to '.
|
|
|
|
'serve this request because it might be part of a redirection '.
|
|
|
|
'attack.',
|
|
|
|
$parameter,
|
|
|
|
get_class($this)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-13 20:24:38 +01:00
|
|
|
if ($this->shouldRequireEnabledUser()) {
|
|
|
|
if ($user->getIsDisabled()) {
|
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 14:01:40 +02:00
|
|
|
$controller = new PhabricatorDisabledUserController();
|
2013-11-13 20:24:38 +01:00
|
|
|
return $this->delegateToController($controller);
|
|
|
|
}
|
2011-05-12 19:06:54 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 02:03:09 +02:00
|
|
|
$auth_class = 'PhabricatorAuthApplication';
|
2014-05-01 19:23:02 +02:00
|
|
|
$auth_application = PhabricatorApplication::getByClass($auth_class);
|
|
|
|
|
|
|
|
// Require partial sessions to finish login before doing anything.
|
|
|
|
if (!$this->shouldAllowPartialSessions()) {
|
|
|
|
if ($user->hasSession() &&
|
|
|
|
$user->getSession()->getIsPartial()) {
|
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 14:01:40 +02:00
|
|
|
$login_controller = new PhabricatorAuthFinishController();
|
2014-05-01 19:23:02 +02:00
|
|
|
$this->setCurrentApplication($auth_application);
|
|
|
|
return $this->delegateToController($login_controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-28 02:02:09 +01:00
|
|
|
// Require users sign Legalpad documents before we check if they have
|
|
|
|
// MFA. If we don't do this, they can get stuck in a state where they
|
|
|
|
// can't add MFA until they sign, and can't sign until they add MFA.
|
|
|
|
// See T13024 and PHI223.
|
|
|
|
$result = $this->requireLegalpadSignatures();
|
|
|
|
if ($result !== null) {
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2014-06-04 01:50:27 +02:00
|
|
|
// Check if the user needs to configure MFA.
|
|
|
|
$need_mfa = $this->shouldRequireMultiFactorEnrollment();
|
|
|
|
$have_mfa = $user->getIsEnrolledInMultiFactor();
|
|
|
|
if ($need_mfa && !$have_mfa) {
|
|
|
|
// Check if the cache is just out of date. Otherwise, roadblock the user
|
|
|
|
// and require MFA enrollment.
|
|
|
|
$user->updateMultiFactorEnrollment();
|
|
|
|
if (!$user->getIsEnrolledInMultiFactor()) {
|
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 14:01:40 +02:00
|
|
|
$mfa_controller = new PhabricatorAuthNeedsMultiFactorController();
|
2014-06-04 01:50:27 +02:00
|
|
|
$this->setCurrentApplication($auth_application);
|
|
|
|
return $this->delegateToController($mfa_controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
if ($this->shouldRequireLogin()) {
|
|
|
|
// This actually means we need either:
|
|
|
|
// - a valid user, or a public controller; and
|
2015-07-03 22:03:33 +02:00
|
|
|
// - permission to see the application; and
|
|
|
|
// - permission to see at least one Space if spaces are configured.
|
2013-01-20 02:40:48 +01:00
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
$allow_public = $this->shouldAllowPublic() &&
|
|
|
|
PhabricatorEnv::getEnvConfig('policy.allow-public');
|
Allow restriction of permitted email domains
Summary:
Allow allowed email addresses to be restricted to certain domains. This implies email must be verified.
This probably isn't QUITE ready for prime-time without a few other tweaks (better administrative tools, notably) but we're nearly there.
Test Plan:
- With no restrictions:
- Registered with OAuth
- Created an account with accountadmin
- Added an email
- With restrictions:
- Tried to OAuth register with a restricted address, was prompted to provide a valid one.
- Tried to OAuth register with a valid address, worked fine.
- Tried to accountadmin a restricted address, got blocked.
- Tried to accountadmin a valid address, worked fine.
- Tried to add a restricted address, blocked.
- Tried to add a valid address, worked fine.
- Created a user with People with an invalid address, got blocked.
- Created a user with People with a valid address, worked fine.
Reviewers: btrahan, csilvers
Reviewed By: csilvers
CC: aran, joe, csilvers
Maniphest Tasks: T1184
Differential Revision: https://secure.phabricator.com/D2581
2012-05-26 15:04:35 +02:00
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
// If this controller isn't public, and the user isn't logged in, require
|
|
|
|
// login.
|
|
|
|
if (!$allow_public && !$user->isLoggedIn()) {
|
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 14:01:40 +02:00
|
|
|
$login_controller = new PhabricatorAuthStartController();
|
2013-10-04 04:05:47 +02:00
|
|
|
$this->setCurrentApplication($auth_application);
|
|
|
|
return $this->delegateToController($login_controller);
|
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 21:47:38 +02:00
|
|
|
}
|
2013-10-04 04:05:47 +02:00
|
|
|
|
|
|
|
if ($user->isLoggedIn()) {
|
|
|
|
if ($this->shouldRequireEmailVerification()) {
|
Improve handling of email verification and "activated" accounts
Summary:
Small step forward which improves existing stuff or lays groudwork for future stuff:
- Currently, to check for email verification, we have to single-query the email address on every page. Instead, denoramlize it into the user object.
- Migrate all the existing users.
- When the user verifies an email, mark them as `isEmailVerified` if the email is their primary email.
- Just make the checks look at the `isEmailVerified` field.
- Add a new check, `isUserActivated()`, to cover email-verified plus disabled. Currently, a non-verified-but-not-disabled user could theoretically use Conduit over SSH, if anyone deployed it. Tighten that up.
- Add an `isApproved` flag, which is always true for now. In a future diff, I want to add a default-on admin approval queue for new accounts, to prevent configuration mistakes. The way it will work is:
- When the queue is enabled, registering users are created with `isApproved = false`.
- Admins are sent an email, "[Phabricator] New User Approval (alincoln)", telling them that a new user is waiting for approval.
- They go to the web UI and approve the user.
- Manually-created accounts are auto-approved.
- The email will have instructions for disabling the queue.
I think this queue will be helpful for new installs and give them peace of mind, and when you go to disable it we have a better opportunity to warn you about exactly what that means.
Generally, I want to improve the default safety of registration, since if you just blindly coast through the path of least resistance right now your install ends up pretty open, and realistically few installs are on VPNs.
Test Plan:
- Ran migration, verified `isEmailVerified` populated correctly.
- Created a new user, checked DB for verified (not verified).
- Verified, checked DB (now verified).
- Used Conduit, People, Diffusion.
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Differential Revision: https://secure.phabricator.com/D7572
2013-11-12 23:37:04 +01:00
|
|
|
if (!$user->getIsEmailVerified()) {
|
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 14:01:40 +02:00
|
|
|
$controller = new PhabricatorMustVerifyEmailController();
|
2013-10-04 04:05:47 +02:00
|
|
|
$this->setCurrentApplication($auth_application);
|
|
|
|
return $this->delegateToController($controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-03 22:03:33 +02:00
|
|
|
// If Spaces are configured, require that the user have access to at
|
|
|
|
// least one. If we don't do this, they'll get confusing error messages
|
|
|
|
// later on.
|
|
|
|
$spaces = PhabricatorSpacesNamespaceQuery::getSpacesExist();
|
|
|
|
if ($spaces) {
|
2015-07-05 13:35:34 +02:00
|
|
|
$viewer_spaces = PhabricatorSpacesNamespaceQuery::getViewerSpaces(
|
2015-07-03 22:03:33 +02:00
|
|
|
$user);
|
|
|
|
if (!$viewer_spaces) {
|
|
|
|
$controller = new PhabricatorSpacesNoAccessController();
|
|
|
|
return $this->delegateToController($controller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
// If the user doesn't have access to the application, don't let them use
|
|
|
|
// any of its controllers. We query the application in order to generate
|
|
|
|
// a policy exception if the viewer doesn't have permission.
|
|
|
|
$application = $this->getCurrentApplication();
|
|
|
|
if ($application) {
|
|
|
|
id(new PhabricatorApplicationQuery())
|
|
|
|
->setViewer($user)
|
|
|
|
->withPHIDs(array($application->getPHID()))
|
|
|
|
->executeOne();
|
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 21:47:38 +02:00
|
|
|
}
|
Tell users to "Wait Patiently" for admin account verification later in the registration process
Summary:
Depends on D18790. Ref T13024. Fixes T8335. Currently, "unapproved" and "disabled" users are bundled together. This prevents users from completing some registration steps (verification, legalpad documents, MFA enrollment) before approval.
Separate approval out and move it to the end so users can do all the required enrollment stuff on their end before we roadblock them.
Test Plan: Required approval, email verification, signatures, and MFA. Registered an account. Verified email, signed documents, enrolled in MFA, and then got prompted to wait for approval.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13024, T8335
Differential Revision: https://secure.phabricator.com/D18791
2017-11-28 02:16:09 +01:00
|
|
|
|
|
|
|
// If users need approval, require they wait here. We do this near the
|
|
|
|
// end so they can take other actions (like verifying email, signing
|
|
|
|
// documents, and enrolling in MFA) while waiting for an admin to take a
|
|
|
|
// look at things. See T13024 for more discussion.
|
|
|
|
if ($this->shouldRequireEnabledUser()) {
|
|
|
|
if ($user->isLoggedIn() && !$user->getIsApproved()) {
|
|
|
|
$controller = new PhabricatorAuthNeedsApprovalController();
|
|
|
|
return $this->delegateToController($controller);
|
|
|
|
}
|
|
|
|
}
|
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 21:47:38 +02:00
|
|
|
}
|
|
|
|
|
2013-10-04 04:05:47 +02:00
|
|
|
// NOTE: We do this last so that users get a login page instead of a 403
|
|
|
|
// if they need to login.
|
2011-05-12 19:06:54 +02:00
|
|
|
if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) {
|
2012-01-15 10:07:56 +01:00
|
|
|
return new Aphront403Response();
|
2011-05-12 19:06:54 +02:00
|
|
|
}
|
2011-01-26 22:21:12 +01:00
|
|
|
}
|
|
|
|
|
2012-08-13 04:19:46 +02:00
|
|
|
public function getApplicationURI($path = '') {
|
|
|
|
if (!$this->getCurrentApplication()) {
|
2015-05-22 09:27:56 +02:00
|
|
|
throw new Exception(pht('No application!'));
|
2012-08-13 04:19:46 +02:00
|
|
|
}
|
2013-05-31 01:37:51 +02:00
|
|
|
return $this->getCurrentApplication()->getApplicationURI($path);
|
2012-08-13 04:19:46 +02:00
|
|
|
}
|
|
|
|
|
2015-09-02 00:52:52 +02:00
|
|
|
public function willSendResponse(AphrontResponse $response) {
|
2012-08-05 23:12:43 +02:00
|
|
|
$request = $this->getRequest();
|
2012-12-12 02:27:25 +01:00
|
|
|
|
2012-08-05 23:12:43 +02:00
|
|
|
if ($response instanceof AphrontDialogResponse) {
|
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 23:52:09 +01:00
|
|
|
if (!$request->isAjax() && !$request->isQuicksand()) {
|
2014-03-21 22:40:05 +01:00
|
|
|
$dialog = $response->getDialog();
|
|
|
|
|
|
|
|
$title = $dialog->getTitle();
|
|
|
|
$short = $dialog->getShortTitle();
|
|
|
|
|
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
|
|
|
$crumbs->addTextCrumb(coalesce($short, $title));
|
|
|
|
|
|
|
|
$page_content = array(
|
|
|
|
$crumbs,
|
|
|
|
$response->buildResponseString(),
|
|
|
|
);
|
|
|
|
|
|
|
|
$view = id(new PhabricatorStandardPageView())
|
|
|
|
->setRequest($request)
|
|
|
|
->setController($this)
|
2014-07-03 03:49:06 +02:00
|
|
|
->setDeviceReady(true)
|
2014-03-21 22:40:05 +01:00
|
|
|
->setTitle($title)
|
|
|
|
->appendChild($page_content);
|
|
|
|
|
|
|
|
$response = id(new AphrontWebpageResponse())
|
|
|
|
->setContent($view->render())
|
|
|
|
->setHTTPResponseCode($response->getHTTPResponseCode());
|
2012-08-05 23:12:43 +02:00
|
|
|
} else {
|
2013-06-17 01:31:14 +02:00
|
|
|
$response->getDialog()->setIsStandalone(true);
|
|
|
|
|
2012-08-05 23:12:43 +02:00
|
|
|
return id(new AphrontAjaxResponse())
|
|
|
|
->setContent(array(
|
|
|
|
'dialog' => $response->buildResponseString(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else if ($response instanceof AphrontRedirectResponse) {
|
2015-03-30 22:02:51 +02:00
|
|
|
if ($request->isAjax() || $request->isQuicksand()) {
|
2012-08-05 23:12:43 +02:00
|
|
|
return id(new AphrontAjaxResponse())
|
|
|
|
->setContent(
|
|
|
|
array(
|
|
|
|
'redirect' => $response->getURI(),
|
Never generate file download forms which point to the CDN domain, tighten "form-action" CSP
Summary:
Depends on D19155. Ref T13094. Ref T4340.
We can't currently implement a strict `form-action 'self'` content security policy because some file downloads rely on a `<form />` which sometimes POSTs to the CDN domain.
Broadly, stop generating these forms. We just redirect instead, and show an interstitial confirm dialog if no CDN domain is configured. This makes the UX for installs with no CDN domain a little worse and the UX for everyone else better.
Then, implement the stricter Content-Security-Policy.
This also removes extra confirm dialogs for downloading Harbormaster build logs and data exports.
Test Plan:
- Went through the plain data export, data export with bulk jobs, ssh key generation, calendar ICS download, Diffusion data, Paste data, Harbormaster log data, and normal file data download workflows with a CDN domain.
- Went through all those workflows again without a CDN domain.
- Grepped for affected symbols (`getCDNURI()`, `getDownloadURI()`).
- Added an evil form to a page, tried to submit it, was rejected.
- Went through the ReCaptcha and Stripe flows again to see if they're submitting any forms.
Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam
Maniphest Tasks: T13094, T4340
Differential Revision: https://secure.phabricator.com/D19156
2018-03-01 00:22:10 +01:00
|
|
|
'close' => $response->getCloseDialogBeforeRedirect(),
|
2012-08-05 23:12:43 +02:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
2014-03-21 22:40:05 +01:00
|
|
|
|
2012-08-05 23:12:43 +02:00
|
|
|
return $response;
|
|
|
|
}
|
|
|
|
|
2015-04-10 19:17:53 +02:00
|
|
|
/**
|
|
|
|
* WARNING: Do not call this in new code.
|
|
|
|
*
|
|
|
|
* @deprecated See "Handles Technical Documentation".
|
|
|
|
*/
|
2012-09-05 04:02:56 +02:00
|
|
|
protected function loadViewerHandles(array $phids) {
|
2013-09-11 21:27:28 +02:00
|
|
|
return id(new PhabricatorHandleQuery())
|
2012-08-15 19:45:06 +02:00
|
|
|
->setViewer($this->getRequest()->getUser())
|
2013-09-11 21:27:28 +02:00
|
|
|
->withPHIDs($phids)
|
|
|
|
->execute();
|
2012-08-15 19:45:06 +02:00
|
|
|
}
|
2012-08-15 22:45:53 +02:00
|
|
|
|
2015-01-15 21:41:26 +01:00
|
|
|
public function buildApplicationMenu() {
|
2012-12-07 22:34:44 +01:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-12-07 22:35:17 +01:00
|
|
|
protected function buildApplicationCrumbs() {
|
|
|
|
$crumbs = array();
|
|
|
|
|
|
|
|
$application = $this->getCurrentApplication();
|
|
|
|
if ($application) {
|
2016-01-28 17:40:22 +01:00
|
|
|
$icon = $application->getIcon();
|
2015-01-26 17:19:22 +01:00
|
|
|
if (!$icon) {
|
|
|
|
$icon = 'fa-puzzle';
|
2012-12-07 22:35:17 +01:00
|
|
|
}
|
|
|
|
|
2015-01-23 20:35:09 +01:00
|
|
|
$crumbs[] = id(new PHUICrumbView())
|
2012-12-07 22:35:17 +01:00
|
|
|
->setHref($this->getApplicationURI())
|
2015-01-26 17:27:54 +01:00
|
|
|
->setName($application->getName())
|
2015-01-26 17:19:22 +01:00
|
|
|
->setIcon($icon);
|
2012-12-07 22:35:17 +01:00
|
|
|
}
|
|
|
|
|
2015-01-23 20:35:09 +01:00
|
|
|
$view = new PHUICrumbsView();
|
2012-12-07 22:35:17 +01:00
|
|
|
foreach ($crumbs as $crumb) {
|
|
|
|
$view->addCrumb($crumb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $view;
|
|
|
|
}
|
|
|
|
|
2013-10-05 00:15:48 +02:00
|
|
|
protected function hasApplicationCapability($capability) {
|
|
|
|
return PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$this->getRequest()->getUser(),
|
|
|
|
$this->getCurrentApplication(),
|
|
|
|
$capability);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function requireApplicationCapability($capability) {
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$this->getRequest()->getUser(),
|
|
|
|
$this->getCurrentApplication(),
|
|
|
|
$capability);
|
|
|
|
}
|
|
|
|
|
2013-10-09 22:52:04 +02:00
|
|
|
protected function explainApplicationCapability(
|
|
|
|
$capability,
|
|
|
|
$positive_message,
|
|
|
|
$negative_message) {
|
|
|
|
|
|
|
|
$can_act = $this->hasApplicationCapability($capability);
|
|
|
|
if ($can_act) {
|
|
|
|
$message = $positive_message;
|
2014-05-12 19:08:32 +02:00
|
|
|
$icon_name = 'fa-play-circle-o lightgreytext';
|
2013-10-09 22:52:04 +02:00
|
|
|
} else {
|
|
|
|
$message = $negative_message;
|
2014-05-12 19:08:32 +02:00
|
|
|
$icon_name = 'fa-lock';
|
2013-10-09 22:52:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$icon = id(new PHUIIconView())
|
2016-01-28 05:38:01 +01:00
|
|
|
->setIcon($icon_name);
|
2013-10-09 22:52:04 +02:00
|
|
|
|
|
|
|
require_celerity_resource('policy-css');
|
|
|
|
|
|
|
|
$phid = $this->getCurrentApplication()->getPHID();
|
|
|
|
$explain_uri = "/policy/explain/{$phid}/{$capability}/";
|
|
|
|
|
|
|
|
$message = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => 'policy-capability-explanation',
|
|
|
|
),
|
|
|
|
array(
|
|
|
|
$icon,
|
|
|
|
javelin_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => $explain_uri,
|
|
|
|
'sigil' => 'workflow',
|
|
|
|
),
|
|
|
|
$message),
|
|
|
|
));
|
|
|
|
|
|
|
|
return array($can_act, $message);
|
2013-10-05 00:15:48 +02:00
|
|
|
}
|
|
|
|
|
2014-01-02 20:59:35 +01:00
|
|
|
public function getDefaultResourceSource() {
|
|
|
|
return 'phabricator';
|
|
|
|
}
|
|
|
|
|
2014-03-21 22:40:05 +01:00
|
|
|
/**
|
|
|
|
* Create a new @{class:AphrontDialogView} with defaults filled in.
|
|
|
|
*
|
|
|
|
* @return AphrontDialogView New dialog.
|
|
|
|
*/
|
2014-10-09 02:23:02 +02:00
|
|
|
public function newDialog() {
|
2014-03-21 22:40:05 +01:00
|
|
|
$submit_uri = new PhutilURI($this->getRequest()->getRequestURI());
|
|
|
|
$submit_uri = $submit_uri->getPath();
|
|
|
|
|
|
|
|
return id(new AphrontDialogView())
|
|
|
|
->setUser($this->getRequest()->getUser())
|
|
|
|
->setSubmitURI($submit_uri);
|
|
|
|
}
|
|
|
|
|
Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
- Don't load named queries multiple times on search pages.
- Don't require extra code to get standard navigation right on mobile.
- Reduce the amount of boilerplate in ListControllers.
- Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
- The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
- It uses this to automatically add crumb actions to the application menu.
- It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
- The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
- SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
- Building menus is generally simpler.
Test Plan:
- Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
- Edited saved queries.
- Tested Differential, Maniphest (no changes).
- Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
|
|
|
public function newPage() {
|
|
|
|
$page = id(new PhabricatorStandardPageView())
|
|
|
|
->setRequest($this->getRequest())
|
2015-11-03 21:51:12 +01:00
|
|
|
->setController($this)
|
|
|
|
->setDeviceReady(true);
|
Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
- Don't load named queries multiple times on search pages.
- Don't require extra code to get standard navigation right on mobile.
- Reduce the amount of boilerplate in ListControllers.
- Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
- The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
- It uses this to automatically add crumb actions to the application menu.
- It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
- The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
- SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
- Building menus is generally simpler.
Test Plan:
- Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
- Edited saved queries.
- Tested Differential, Maniphest (no changes).
- Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
|
|
|
|
|
|
|
$application = $this->getCurrentApplication();
|
|
|
|
if ($application) {
|
|
|
|
$page->setApplicationName($application->getName());
|
|
|
|
if ($application->getTitleGlyph()) {
|
|
|
|
$page->setGlyph($application->getTitleGlyph());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$viewer = $this->getRequest()->getUser();
|
|
|
|
if ($viewer) {
|
|
|
|
$page->setUser($viewer);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $page;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function newApplicationMenu() {
|
|
|
|
return id(new PHUIApplicationMenuView())
|
2016-03-05 23:45:56 +01:00
|
|
|
->setViewer($this->getViewer());
|
|
|
|
}
|
|
|
|
|
2017-05-16 17:50:55 +02:00
|
|
|
public function newCurtainView($object = null) {
|
2016-03-05 23:45:56 +01:00
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
2016-06-21 02:49:38 +02:00
|
|
|
$action_id = celerity_generate_unique_node_id();
|
|
|
|
|
2016-03-05 23:45:56 +01:00
|
|
|
$action_list = id(new PhabricatorActionListView())
|
2016-06-21 02:49:38 +02:00
|
|
|
->setViewer($viewer)
|
|
|
|
->setID($action_id);
|
2016-03-06 19:08:49 +01:00
|
|
|
|
|
|
|
// NOTE: Applications (objects of class PhabricatorApplication) can't
|
|
|
|
// currently be set here, although they don't need any of the extensions
|
|
|
|
// anyway. This should probably work differently than it does, though.
|
2017-06-12 20:24:17 +02:00
|
|
|
if ($object) {
|
|
|
|
if ($object instanceof PhabricatorLiskDAO) {
|
|
|
|
$action_list->setObject($object);
|
|
|
|
}
|
2016-03-06 19:08:49 +01:00
|
|
|
}
|
2016-03-05 23:45:56 +01:00
|
|
|
|
|
|
|
$curtain = id(new PHUICurtainView())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->setActionList($action_list);
|
|
|
|
|
2017-05-16 17:50:55 +02:00
|
|
|
if ($object) {
|
|
|
|
$panels = PHUICurtainExtension::buildExtensionPanels($viewer, $object);
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
$curtain->addPanel($panel);
|
|
|
|
}
|
2016-03-05 23:45:56 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $curtain;
|
Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
- Don't load named queries multiple times on search pages.
- Don't require extra code to get standard navigation right on mobile.
- Reduce the amount of boilerplate in ListControllers.
- Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
- The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
- It uses this to automatically add crumb actions to the application menu.
- It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
- The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
- SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
- Building menus is generally simpler.
Test Plan:
- Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
- Edited saved queries.
- Tested Differential, Maniphest (no changes).
- Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
|
|
|
}
|
|
|
|
|
2014-11-13 23:44:55 +01:00
|
|
|
protected function buildTransactionTimeline(
|
2014-11-14 23:50:50 +01:00
|
|
|
PhabricatorApplicationTransactionInterface $object,
|
2019-08-24 18:43:27 +02:00
|
|
|
PhabricatorApplicationTransactionQuery $query = null,
|
2014-12-04 22:58:52 +01:00
|
|
|
PhabricatorMarkupEngine $engine = null,
|
Remove "willRenderTimeline()" from ApplicationTransactionInterface
Summary:
Depends on D19914. Ref T11351. Some of the Phoilo rabbit holes go very deep.
`PhabricatorApplicationTransactionInterface` currently requires you to implement `willRenderTimeline()`. Almost every object just implements this as `return $timeline`; only Pholio, Diffusion, and Differential specialize it. In all cases, they are specializing it mostly to render inline comments.
The actual implementations are a bit of a weird mess and the way the data is threaded through the call stack is weird and not very modern.
Try to clean this up:
- Stop requiring `willRenderTimeline()` to be implemented.
- Stop requiring `getApplicationTransactionViewObject()` to be implemented (only the three above, plus Legalpad, implement this, and Legalpad's implementation is a no-op). These two methods are inherently pretty coupled for almost any reasonable thing you might want to do with the timeline.
- Simplify the handling of "renderdata" and call it "View Data". This is additional information about the current view of the transaction timeline that is required to render it correctly. This is only used in Differential, to decide if we can link an inline comment to an anchor on the same page or should link it to another page. We could perhaps do this on the client instead, but having this data doesn't seem inherently bad to me.
- If objects want to customize timeline rendering, they now implement `PhabricatorTimelineInterface` and provide a `TimelineEngine` which gets a nice formal stack.
This leaves a lot of empty `willRenderTimeline()` implementations hanging around. I'll remove these in the next change, it's just going to be deleting a couple dozen copies of an identical empty method implementation.
Test Plan:
- Viewed audits, revisions, and mocks with inline comments.
- Used "Show Older" to page a revision back in history (this is relevant for "View Data").
- Grepped for symbols: willRenderTimeline, getApplicationTransactionViewObject, Legalpad classes.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T11351
Differential Revision: https://secure.phabricator.com/D19918
2018-12-19 20:52:53 +01:00
|
|
|
$view_data = array()) {
|
2014-11-13 23:44:55 +01:00
|
|
|
|
Remove "willRenderTimeline()" from ApplicationTransactionInterface
Summary:
Depends on D19914. Ref T11351. Some of the Phoilo rabbit holes go very deep.
`PhabricatorApplicationTransactionInterface` currently requires you to implement `willRenderTimeline()`. Almost every object just implements this as `return $timeline`; only Pholio, Diffusion, and Differential specialize it. In all cases, they are specializing it mostly to render inline comments.
The actual implementations are a bit of a weird mess and the way the data is threaded through the call stack is weird and not very modern.
Try to clean this up:
- Stop requiring `willRenderTimeline()` to be implemented.
- Stop requiring `getApplicationTransactionViewObject()` to be implemented (only the three above, plus Legalpad, implement this, and Legalpad's implementation is a no-op). These two methods are inherently pretty coupled for almost any reasonable thing you might want to do with the timeline.
- Simplify the handling of "renderdata" and call it "View Data". This is additional information about the current view of the transaction timeline that is required to render it correctly. This is only used in Differential, to decide if we can link an inline comment to an anchor on the same page or should link it to another page. We could perhaps do this on the client instead, but having this data doesn't seem inherently bad to me.
- If objects want to customize timeline rendering, they now implement `PhabricatorTimelineInterface` and provide a `TimelineEngine` which gets a nice formal stack.
This leaves a lot of empty `willRenderTimeline()` implementations hanging around. I'll remove these in the next change, it's just going to be deleting a couple dozen copies of an identical empty method implementation.
Test Plan:
- Viewed audits, revisions, and mocks with inline comments.
- Used "Show Older" to page a revision back in history (this is relevant for "View Data").
- Grepped for symbols: willRenderTimeline, getApplicationTransactionViewObject, Legalpad classes.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T11351
Differential Revision: https://secure.phabricator.com/D19918
2018-12-19 20:52:53 +01:00
|
|
|
$request = $this->getRequest();
|
|
|
|
$viewer = $this->getViewer();
|
2014-11-13 23:44:55 +01:00
|
|
|
$xaction = $object->getApplicationTransactionTemplate();
|
|
|
|
|
2019-08-24 18:43:27 +02:00
|
|
|
if (!$query) {
|
|
|
|
$query = PhabricatorApplicationTransactionQuery::newQueryForObject(
|
|
|
|
$object);
|
|
|
|
if (!$query) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Unable to find transaction query for object of class "%s".',
|
|
|
|
get_class($object)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-02 22:10:29 +01:00
|
|
|
$pager = id(new AphrontCursorPagerView())
|
Remove "willRenderTimeline()" from ApplicationTransactionInterface
Summary:
Depends on D19914. Ref T11351. Some of the Phoilo rabbit holes go very deep.
`PhabricatorApplicationTransactionInterface` currently requires you to implement `willRenderTimeline()`. Almost every object just implements this as `return $timeline`; only Pholio, Diffusion, and Differential specialize it. In all cases, they are specializing it mostly to render inline comments.
The actual implementations are a bit of a weird mess and the way the data is threaded through the call stack is weird and not very modern.
Try to clean this up:
- Stop requiring `willRenderTimeline()` to be implemented.
- Stop requiring `getApplicationTransactionViewObject()` to be implemented (only the three above, plus Legalpad, implement this, and Legalpad's implementation is a no-op). These two methods are inherently pretty coupled for almost any reasonable thing you might want to do with the timeline.
- Simplify the handling of "renderdata" and call it "View Data". This is additional information about the current view of the transaction timeline that is required to render it correctly. This is only used in Differential, to decide if we can link an inline comment to an anchor on the same page or should link it to another page. We could perhaps do this on the client instead, but having this data doesn't seem inherently bad to me.
- If objects want to customize timeline rendering, they now implement `PhabricatorTimelineInterface` and provide a `TimelineEngine` which gets a nice formal stack.
This leaves a lot of empty `willRenderTimeline()` implementations hanging around. I'll remove these in the next change, it's just going to be deleting a couple dozen copies of an identical empty method implementation.
Test Plan:
- Viewed audits, revisions, and mocks with inline comments.
- Used "Show Older" to page a revision back in history (this is relevant for "View Data").
- Grepped for symbols: willRenderTimeline, getApplicationTransactionViewObject, Legalpad classes.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T11351
Differential Revision: https://secure.phabricator.com/D19918
2018-12-19 20:52:53 +01:00
|
|
|
->readFromRequest($request)
|
2014-12-02 22:10:29 +01:00
|
|
|
->setURI(new PhutilURI(
|
|
|
|
'/transactions/showolder/'.$object->getPHID().'/'));
|
|
|
|
|
2014-11-13 23:44:55 +01:00
|
|
|
$xactions = $query
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withObjectPHIDs(array($object->getPHID()))
|
|
|
|
->needComments(true)
|
2014-12-02 22:10:29 +01:00
|
|
|
->executeWithCursorPager($pager);
|
|
|
|
$xactions = array_reverse($xactions);
|
2014-11-13 23:44:55 +01:00
|
|
|
|
Remove "willRenderTimeline()" from ApplicationTransactionInterface
Summary:
Depends on D19914. Ref T11351. Some of the Phoilo rabbit holes go very deep.
`PhabricatorApplicationTransactionInterface` currently requires you to implement `willRenderTimeline()`. Almost every object just implements this as `return $timeline`; only Pholio, Diffusion, and Differential specialize it. In all cases, they are specializing it mostly to render inline comments.
The actual implementations are a bit of a weird mess and the way the data is threaded through the call stack is weird and not very modern.
Try to clean this up:
- Stop requiring `willRenderTimeline()` to be implemented.
- Stop requiring `getApplicationTransactionViewObject()` to be implemented (only the three above, plus Legalpad, implement this, and Legalpad's implementation is a no-op). These two methods are inherently pretty coupled for almost any reasonable thing you might want to do with the timeline.
- Simplify the handling of "renderdata" and call it "View Data". This is additional information about the current view of the transaction timeline that is required to render it correctly. This is only used in Differential, to decide if we can link an inline comment to an anchor on the same page or should link it to another page. We could perhaps do this on the client instead, but having this data doesn't seem inherently bad to me.
- If objects want to customize timeline rendering, they now implement `PhabricatorTimelineInterface` and provide a `TimelineEngine` which gets a nice formal stack.
This leaves a lot of empty `willRenderTimeline()` implementations hanging around. I'll remove these in the next change, it's just going to be deleting a couple dozen copies of an identical empty method implementation.
Test Plan:
- Viewed audits, revisions, and mocks with inline comments.
- Used "Show Older" to page a revision back in history (this is relevant for "View Data").
- Grepped for symbols: willRenderTimeline, getApplicationTransactionViewObject, Legalpad classes.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T11351
Differential Revision: https://secure.phabricator.com/D19918
2018-12-19 20:52:53 +01:00
|
|
|
$timeline_engine = PhabricatorTimelineEngine::newForObject($object)
|
|
|
|
->setViewer($viewer)
|
|
|
|
->setTransactions($xactions)
|
|
|
|
->setViewData($view_data);
|
|
|
|
|
|
|
|
$view = $timeline_engine->buildTimelineView();
|
|
|
|
|
2014-11-13 23:44:55 +01:00
|
|
|
if ($engine) {
|
|
|
|
foreach ($xactions as $xaction) {
|
|
|
|
if ($xaction->getComment()) {
|
|
|
|
$engine->addObject(
|
|
|
|
$xaction->getComment(),
|
|
|
|
PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$engine->process();
|
|
|
|
$view->setMarkupEngine($engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
$timeline = $view
|
2014-12-04 22:58:52 +01:00
|
|
|
->setPager($pager)
|
2014-12-11 19:27:28 +01:00
|
|
|
->setQuoteTargetID($this->getRequest()->getStr('quoteTargetID'))
|
|
|
|
->setQuoteRef($this->getRequest()->getStr('quoteRef'));
|
2014-11-13 23:44:55 +01:00
|
|
|
|
|
|
|
return $timeline;
|
|
|
|
}
|
|
|
|
|
Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
- Don't load named queries multiple times on search pages.
- Don't require extra code to get standard navigation right on mobile.
- Reduce the amount of boilerplate in ListControllers.
- Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
- The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
- It uses this to automatically add crumb actions to the application menu.
- It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
- The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
- SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
- Building menus is generally simpler.
Test Plan:
- Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
- Edited saved queries.
- Tested Differential, Maniphest (no changes).
- Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
|
|
|
|
Implement a basic version of ApplicationEditor in Paste
Summary:
Ref T9132. Ref T4768. This is a rough v0 of ApplicationEditor, which replaces the edit workflow in Paste.
This mostly looks and works like ApplicationSearch, and is heavily modeled on it.
Roughly, we define a set of editable fields and the ApplicationEditor stuff builds everything else.
This has no functional changes, except:
- I removed "Fork Paste" since I don't think it's particularly useful now that pastes are editable. We could restore it if users miss it.
- Subscribers are now editable.
- Form field order is a little goofy (this will be fixed in a future diff).
- Subscribers and projects are now race-resistant.
The race-resistance works like this: instead of submitting just the new value ("subscribers=apple, dog") and doing a set operation ("set subscribers = apple, dog"), we submit the old and new values ("original=apple" + "new=apple, dog") then apply the user's changes as an add + remove ("add=dog", "remove=<none>"). This means that two users who do "Edit Paste" at around the same time and each add or remove a couple of subscribers won't overwrite each other, unless they actually add or remove the exact same subscribers (in which case their edits legitimately conflict). Previously, the last user to save would win, and whatever was in their field would overwrite the prior state, potentially losing the first user's edits.
Test Plan:
- Created pastes.
- Created pastes via API.
- Edited pastes.
- Edited every field.
- Opened a paste in two windows and did project/subscriber edits in each, saved in arbitrary order, had edits respected.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4768, T9132
Differential Revision: https://secure.phabricator.com/D14390
2015-11-03 03:58:32 +01:00
|
|
|
public function buildApplicationCrumbsForEditEngine() {
|
2017-10-09 19:48:01 +02:00
|
|
|
// TODO: This is kind of gross, I'm basically just making this public so
|
Implement a basic version of ApplicationEditor in Paste
Summary:
Ref T9132. Ref T4768. This is a rough v0 of ApplicationEditor, which replaces the edit workflow in Paste.
This mostly looks and works like ApplicationSearch, and is heavily modeled on it.
Roughly, we define a set of editable fields and the ApplicationEditor stuff builds everything else.
This has no functional changes, except:
- I removed "Fork Paste" since I don't think it's particularly useful now that pastes are editable. We could restore it if users miss it.
- Subscribers are now editable.
- Form field order is a little goofy (this will be fixed in a future diff).
- Subscribers and projects are now race-resistant.
The race-resistance works like this: instead of submitting just the new value ("subscribers=apple, dog") and doing a set operation ("set subscribers = apple, dog"), we submit the old and new values ("original=apple" + "new=apple, dog") then apply the user's changes as an add + remove ("add=dog", "remove=<none>"). This means that two users who do "Edit Paste" at around the same time and each add or remove a couple of subscribers won't overwrite each other, unless they actually add or remove the exact same subscribers (in which case their edits legitimately conflict). Previously, the last user to save would win, and whatever was in their field would overwrite the prior state, potentially losing the first user's edits.
Test Plan:
- Created pastes.
- Created pastes via API.
- Edited pastes.
- Edited every field.
- Opened a paste in two windows and did project/subscriber edits in each, saved in arbitrary order, had edits respected.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4768, T9132
Differential Revision: https://secure.phabricator.com/D14390
2015-11-03 03:58:32 +01:00
|
|
|
// I can use it in EditEngine. We could do this without making it public
|
|
|
|
// by using controller delegation, or make it properly public.
|
|
|
|
return $this->buildApplicationCrumbs();
|
|
|
|
}
|
|
|
|
|
2017-11-28 01:10:38 +01:00
|
|
|
private function requireLegalpadSignatures() {
|
2017-11-28 02:02:09 +01:00
|
|
|
if (!$this->shouldRequireLogin()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-11-28 01:10:38 +01:00
|
|
|
if ($this->shouldAllowLegallyNonCompliantUsers()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
if (!$viewer->hasSession()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$session = $viewer->getSession();
|
|
|
|
if ($session->getIsPartial()) {
|
|
|
|
// If the user hasn't made it through MFA yet, require they survive
|
|
|
|
// MFA first.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($session->getSignedLegalpadDocuments()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$viewer->isLoggedIn()) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-12-03 01:11:58 +01:00
|
|
|
$must_sign_docs = array();
|
|
|
|
$sign_docs = array();
|
|
|
|
|
2017-11-28 01:10:38 +01:00
|
|
|
$legalpad_class = 'PhabricatorLegalpadApplication';
|
|
|
|
$legalpad_installed = PhabricatorApplication::isClassInstalledForViewer(
|
|
|
|
$legalpad_class,
|
|
|
|
$viewer);
|
2017-12-03 01:11:58 +01:00
|
|
|
if ($legalpad_installed) {
|
|
|
|
$sign_docs = id(new LegalpadDocumentQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withSignatureRequired(1)
|
|
|
|
->needViewerSignatures(true)
|
|
|
|
->setOrder('oldest')
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
foreach ($sign_docs as $sign_doc) {
|
|
|
|
if (!$sign_doc->getUserSignature($viewer->getPHID())) {
|
|
|
|
$must_sign_docs[] = $sign_doc;
|
|
|
|
}
|
2017-11-28 01:10:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$must_sign_docs) {
|
|
|
|
// If nothing needs to be signed (either because there are no documents
|
|
|
|
// which require a signature, or because the user has already signed
|
|
|
|
// all of them) mark the session as good and continue.
|
|
|
|
$engine = id(new PhabricatorAuthSessionEngine())
|
|
|
|
->signLegalpadDocuments($viewer, $sign_docs);
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$request = $this->getRequest();
|
|
|
|
$request->setURIMap(
|
|
|
|
array(
|
|
|
|
'id' => head($must_sign_docs)->getID(),
|
|
|
|
));
|
|
|
|
|
|
|
|
$application = PhabricatorApplication::getByClass($legalpad_class);
|
|
|
|
$this->setCurrentApplication($application);
|
|
|
|
|
|
|
|
$controller = new LegalpadDocumentSignController();
|
2019-03-22 17:32:13 +01:00
|
|
|
$controller->setIsSessionGate(true);
|
2017-11-28 01:10:38 +01:00
|
|
|
return $this->delegateToController($controller);
|
|
|
|
}
|
|
|
|
|
Implement a basic version of ApplicationEditor in Paste
Summary:
Ref T9132. Ref T4768. This is a rough v0 of ApplicationEditor, which replaces the edit workflow in Paste.
This mostly looks and works like ApplicationSearch, and is heavily modeled on it.
Roughly, we define a set of editable fields and the ApplicationEditor stuff builds everything else.
This has no functional changes, except:
- I removed "Fork Paste" since I don't think it's particularly useful now that pastes are editable. We could restore it if users miss it.
- Subscribers are now editable.
- Form field order is a little goofy (this will be fixed in a future diff).
- Subscribers and projects are now race-resistant.
The race-resistance works like this: instead of submitting just the new value ("subscribers=apple, dog") and doing a set operation ("set subscribers = apple, dog"), we submit the old and new values ("original=apple" + "new=apple, dog") then apply the user's changes as an add + remove ("add=dog", "remove=<none>"). This means that two users who do "Edit Paste" at around the same time and each add or remove a couple of subscribers won't overwrite each other, unless they actually add or remove the exact same subscribers (in which case their edits legitimately conflict). Previously, the last user to save would win, and whatever was in their field would overwrite the prior state, potentially losing the first user's edits.
Test Plan:
- Created pastes.
- Created pastes via API.
- Edited pastes.
- Edited every field.
- Opened a paste in two windows and did project/subscriber edits in each, saved in arbitrary order, had edits respected.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T4768, T9132
Differential Revision: https://secure.phabricator.com/D14390
2015-11-03 03:58:32 +01:00
|
|
|
|
Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
- Don't load named queries multiple times on search pages.
- Don't require extra code to get standard navigation right on mobile.
- Reduce the amount of boilerplate in ListControllers.
- Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
- The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
- It uses this to automatically add crumb actions to the application menu.
- It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
- The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
- SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
- Building menus is generally simpler.
Test Plan:
- Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
- Edited saved queries.
- Tested Differential, Maniphest (no changes).
- Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
2015-11-02 21:06:28 +01:00
|
|
|
/* -( Deprecated )--------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DEPRECATED. Use @{method:newPage}.
|
|
|
|
*/
|
|
|
|
public function buildStandardPageView() {
|
|
|
|
return $this->newPage();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DEPRECATED. Use @{method:newPage}.
|
|
|
|
*/
|
|
|
|
public function buildStandardPageResponse($view, array $data) {
|
|
|
|
$page = $this->buildStandardPageView();
|
|
|
|
$page->appendChild($view);
|
|
|
|
return $page->produceAphrontResponse();
|
|
|
|
}
|
|
|
|
|
2011-01-16 22:51:39 +01:00
|
|
|
}
|