2012-08-05 14:12:43 -07:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorApplicationAuth extends PhabricatorApplication {
|
|
|
|
|
2013-01-29 09:14:03 -08:00
|
|
|
public function canUninstall() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-06-16 10:15:16 -07:00
|
|
|
public function getBaseURI() {
|
|
|
|
return '/auth/';
|
|
|
|
}
|
|
|
|
|
2013-06-18 10:02:34 -07:00
|
|
|
public function getIconName() {
|
|
|
|
return 'authentication';
|
|
|
|
}
|
|
|
|
|
2013-06-20 11:18:48 -07:00
|
|
|
public function getHelpURI() {
|
|
|
|
// NOTE: Although reasonable help exists for this in "Configuring Accounts
|
|
|
|
// and Registration", specifying a help URI here means we get the menu
|
|
|
|
// item in all the login/link interfaces, which is confusing and not
|
|
|
|
// helpful.
|
|
|
|
|
|
|
|
// TODO: Special case this, or split the auth and auth administration
|
|
|
|
// applications?
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-08-05 14:12:43 -07:00
|
|
|
public function buildMainMenuItems(
|
|
|
|
PhabricatorUser $user,
|
2012-08-06 12:46:51 -07:00
|
|
|
PhabricatorController $controller = null) {
|
2012-08-05 14:12:43 -07:00
|
|
|
|
|
|
|
$items = array();
|
|
|
|
|
2012-08-10 12:11:24 -07:00
|
|
|
if ($user->isLoggedIn()) {
|
2013-06-18 11:05:28 -07:00
|
|
|
$item = id(new PHUIListItemView())
|
|
|
|
->addClass('core-menu-item')
|
|
|
|
->setName(pht('Log Out'))
|
2014-01-31 09:10:32 -08:00
|
|
|
->setIcon('logout-sm')
|
2013-06-18 11:05:28 -07:00
|
|
|
->setWorkflow(true)
|
|
|
|
->setHref('/logout/')
|
2014-01-28 20:18:01 -08:00
|
|
|
->setSelected(($controller instanceof PhabricatorLogoutController))
|
|
|
|
->setOrder(900);
|
2012-08-05 14:12:43 -07:00
|
|
|
$items[] = $item;
|
2013-11-13 11:24:38 -08:00
|
|
|
} else {
|
|
|
|
if ($controller instanceof PhabricatorAuthController) {
|
|
|
|
// Don't show the "Login" item on auth controllers, since they're
|
|
|
|
// generally all related to logging in anyway.
|
|
|
|
} else {
|
|
|
|
$item = id(new PHUIListItemView())
|
|
|
|
->addClass('core-menu-item')
|
|
|
|
->setName(pht('Log In'))
|
|
|
|
// TODO: Login icon?
|
|
|
|
->setIcon('power')
|
2014-01-28 20:18:01 -08:00
|
|
|
->setHref('/auth/start/')
|
|
|
|
->setOrder(900);
|
2013-11-13 11:24:38 -08:00
|
|
|
$items[] = $item;
|
|
|
|
}
|
2012-08-05 14:12:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return $items;
|
|
|
|
}
|
|
|
|
|
2013-06-20 11:18:11 -07:00
|
|
|
public function getApplicationGroup() {
|
|
|
|
return self::GROUP_ADMIN;
|
2013-06-17 10:50:43 -07:00
|
|
|
}
|
|
|
|
|
New Registration Workflow
Summary:
Currently, registration and authentication are pretty messy. Two concrete problems:
- The `PhabricatorLDAPRegistrationController` and `PhabricatorOAuthDefaultRegistrationController` controllers are giant copy/pastes of one another. This is really bad.
- We can't practically implement OpenID because we can't reissue the authentication request.
Additionally, the OAuth registration controller can be replaced wholesale by config, which is a huge API surface area and a giant mess.
Broadly, the problem right now is that registration does too much: we hand it some set of indirect credentials (like OAuth tokens) and expect it to take those the entire way to a registered user. Instead, break registration into smaller steps:
- User authenticates with remote service.
- Phabricator pulls information (remote account ID, username, email, real name, profile picture, etc) from the remote service and saves it as `PhabricatorUserCredentials`.
- Phabricator hands the `PhabricatorUserCredentials` to the registration form, which is agnostic about where they originate from: it can process LDAP credentials, OAuth credentials, plain old email credentials, HTTP basic auth credentials, etc.
This doesn't do anything yet -- there is no way to create credentials objects (and no storage patch), but I wanted to get any initial feedback, especially about the event call for T2394. In particular, I think the implementation would look something like this:
$profile = $event->getValue('profile')
$username = $profile->getDefaultUsername();
$is_employee = is_this_a_facebook_employee($username);
if (!$is_employee) {
throw new Exception("You are not employed at Facebook.");
}
$fbid = get_fbid_for_facebook_username($username);
$profile->setDefaultEmail($fbid);
$profile->setCanEditUsername(false);
$profile->setCanEditEmail(false);
$profile->setCanEditRealName(false);
$profile->setShouldVerifyEmail(true);
Seem reasonable?
Test Plan: N/A yet, probably fatals.
Reviewers: vrana, btrahan, codeblock, chad
Reviewed By: btrahan
CC: aran, asherkin, nh, wez
Maniphest Tasks: T1536, T2394
Differential Revision: https://secure.phabricator.com/D4647
2013-06-16 10:13:49 -07:00
|
|
|
public function getRoutes() {
|
|
|
|
return array(
|
|
|
|
'/auth/' => array(
|
2013-06-19 15:00:37 -07:00
|
|
|
'' => 'PhabricatorAuthListController',
|
2013-06-17 10:52:38 -07:00
|
|
|
'config/' => array(
|
|
|
|
'new/' => 'PhabricatorAuthNewController',
|
|
|
|
'new/(?P<className>[^/]+)/' => 'PhabricatorAuthEditController',
|
|
|
|
'edit/(?P<id>\d+)/' => 'PhabricatorAuthEditController',
|
2013-06-17 10:54:08 -07:00
|
|
|
'(?P<action>enable|disable)/(?P<id>\d+)/' =>
|
|
|
|
'PhabricatorAuthDisableController',
|
2013-06-17 10:52:38 -07:00
|
|
|
),
|
2014-02-23 16:39:24 -08:00
|
|
|
'login/(?P<pkey>[^/]+)/(?:(?P<extra>[^/]+)/)?' =>
|
|
|
|
'PhabricatorAuthLoginController',
|
Add password authentication and registration to new registration
Summary:
Ref T1536. Ref T1930. Code is not reachable.
This provides password authentication and registration on the new provider/adapter framework.
I sort of cheated a little bit and don't really route any password logic through the adapter (instead, this provider uses an empty adapter and just sets the type/domain on it). I think the right way to do this //conceptually// is to treat username/passwords as an external black box which the adapter communicates with. However, this creates a lot of practical implementation and UX problems:
- There would basically be two steps -- in the first one, you interact with the "password black box", which behaves like an OAuth provider. This produces some ExternalAccount associated with the username/password pair, then we go into normal registration.
- In normal registration, we'd proceed normally.
This means:
- The registration flow would be split into two parts, one where you select a username/password (interacting with the black box) and one where you actually register (interacting with the generic flow). This is unusual and probably confusing for users.
- We would need to do a lot of re-hashing of passwords, since passwords currently depend on the username and user PHID, which won't exist yet during registration or the "black box" phase. This is a big mess I don't want to deal with.
- We hit a weird condition where two users complete step 1 with the same username but don't complete step 2 yet. The box knows about two different copies of the username, with two different passwords. When we arrive at step 2 the second time we have a lot of bad choices about how to reoslve it, most of which create security problems. The most stragihtforward and "pure" way to resolve the issues is to put password-auth usernames in a separate space, but this would be incredibly confusuing to users (your login name might not be the same as your username, which is bizarre).
- If we change this, we need to update all the other password-related code, which I don't want to bother with (at least for now).
Instead, let registration know about a "default" registration controller (which is always password, if enabled), and let it require a password. This gives us a much simpler (albeit slightly less pure) implementation:
- All the fields are on one form.
- Password adapter is just a shell.
- Password provider does the heavy lifting.
We might make this more pure at some point, but I'm generally pretty satisfied with this.
This doesn't implement the brute-force CAPTCHA protection, that will be coming soon.
Test Plan: Registered with password only and logged in with a password. Hit various error conditions.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, chad
Maniphest Tasks: T1536, T1930
Differential Revision: https://secure.phabricator.com/D6164
2013-06-16 10:15:49 -07:00
|
|
|
'register/(?:(?P<akey>[^/]+)/)?' => 'PhabricatorAuthRegisterController',
|
2013-06-16 10:15:16 -07:00
|
|
|
'start/' => 'PhabricatorAuthStartController',
|
2013-06-16 10:15:33 -07:00
|
|
|
'validate/' => 'PhabricatorAuthValidateController',
|
2013-06-17 06:12:45 -07:00
|
|
|
'unlink/(?P<pkey>[^/]+)/' => 'PhabricatorAuthUnlinkController',
|
2013-06-24 15:58:27 -07:00
|
|
|
'(?P<action>link|refresh)/(?P<pkey>[^/]+)/'
|
|
|
|
=> 'PhabricatorAuthLinkController',
|
2013-06-17 06:12:45 -07:00
|
|
|
'confirmlink/(?P<akey>[^/]+)/'
|
|
|
|
=> 'PhabricatorAuthConfirmLinkController',
|
2014-03-17 15:02:01 -07:00
|
|
|
'session/terminate/(?P<id>[^/]+)/'
|
|
|
|
=> 'PhabricatorAuthTerminateSessionController',
|
New Registration Workflow
Summary:
Currently, registration and authentication are pretty messy. Two concrete problems:
- The `PhabricatorLDAPRegistrationController` and `PhabricatorOAuthDefaultRegistrationController` controllers are giant copy/pastes of one another. This is really bad.
- We can't practically implement OpenID because we can't reissue the authentication request.
Additionally, the OAuth registration controller can be replaced wholesale by config, which is a huge API surface area and a giant mess.
Broadly, the problem right now is that registration does too much: we hand it some set of indirect credentials (like OAuth tokens) and expect it to take those the entire way to a registered user. Instead, break registration into smaller steps:
- User authenticates with remote service.
- Phabricator pulls information (remote account ID, username, email, real name, profile picture, etc) from the remote service and saves it as `PhabricatorUserCredentials`.
- Phabricator hands the `PhabricatorUserCredentials` to the registration form, which is agnostic about where they originate from: it can process LDAP credentials, OAuth credentials, plain old email credentials, HTTP basic auth credentials, etc.
This doesn't do anything yet -- there is no way to create credentials objects (and no storage patch), but I wanted to get any initial feedback, especially about the event call for T2394. In particular, I think the implementation would look something like this:
$profile = $event->getValue('profile')
$username = $profile->getDefaultUsername();
$is_employee = is_this_a_facebook_employee($username);
if (!$is_employee) {
throw new Exception("You are not employed at Facebook.");
}
$fbid = get_fbid_for_facebook_username($username);
$profile->setDefaultEmail($fbid);
$profile->setCanEditUsername(false);
$profile->setCanEditEmail(false);
$profile->setCanEditRealName(false);
$profile->setShouldVerifyEmail(true);
Seem reasonable?
Test Plan: N/A yet, probably fatals.
Reviewers: vrana, btrahan, codeblock, chad
Reviewed By: btrahan
CC: aran, asherkin, nh, wez
Maniphest Tasks: T1536, T2394
Differential Revision: https://secure.phabricator.com/D4647
2013-06-16 10:13:49 -07:00
|
|
|
),
|
2013-06-16 10:18:45 -07:00
|
|
|
|
2013-06-21 06:11:57 -07:00
|
|
|
'/oauth/(?P<provider>\w+)/login/'
|
|
|
|
=> 'PhabricatorAuthOldOAuthRedirectController',
|
2013-06-19 01:33:27 -07:00
|
|
|
|
2013-06-16 10:18:45 -07:00
|
|
|
'/login/' => array(
|
2013-06-19 01:33:27 -07:00
|
|
|
'' => 'PhabricatorAuthStartController',
|
2013-06-16 10:18:45 -07:00
|
|
|
'email/' => 'PhabricatorEmailLoginController',
|
|
|
|
'etoken/(?P<token>\w+)/' => 'PhabricatorEmailTokenController',
|
|
|
|
'refresh/' => 'PhabricatorRefreshCSRFController',
|
|
|
|
'mustverify/' => 'PhabricatorMustVerifyEmailController',
|
|
|
|
),
|
|
|
|
|
2013-07-10 18:53:09 -07:00
|
|
|
'/emailverify/(?P<code>[^/]+)/' =>
|
|
|
|
'PhabricatorEmailVerificationController',
|
|
|
|
|
2013-06-16 10:18:45 -07:00
|
|
|
'/logout/' => 'PhabricatorLogoutController',
|
New Registration Workflow
Summary:
Currently, registration and authentication are pretty messy. Two concrete problems:
- The `PhabricatorLDAPRegistrationController` and `PhabricatorOAuthDefaultRegistrationController` controllers are giant copy/pastes of one another. This is really bad.
- We can't practically implement OpenID because we can't reissue the authentication request.
Additionally, the OAuth registration controller can be replaced wholesale by config, which is a huge API surface area and a giant mess.
Broadly, the problem right now is that registration does too much: we hand it some set of indirect credentials (like OAuth tokens) and expect it to take those the entire way to a registered user. Instead, break registration into smaller steps:
- User authenticates with remote service.
- Phabricator pulls information (remote account ID, username, email, real name, profile picture, etc) from the remote service and saves it as `PhabricatorUserCredentials`.
- Phabricator hands the `PhabricatorUserCredentials` to the registration form, which is agnostic about where they originate from: it can process LDAP credentials, OAuth credentials, plain old email credentials, HTTP basic auth credentials, etc.
This doesn't do anything yet -- there is no way to create credentials objects (and no storage patch), but I wanted to get any initial feedback, especially about the event call for T2394. In particular, I think the implementation would look something like this:
$profile = $event->getValue('profile')
$username = $profile->getDefaultUsername();
$is_employee = is_this_a_facebook_employee($username);
if (!$is_employee) {
throw new Exception("You are not employed at Facebook.");
}
$fbid = get_fbid_for_facebook_username($username);
$profile->setDefaultEmail($fbid);
$profile->setCanEditUsername(false);
$profile->setCanEditEmail(false);
$profile->setCanEditRealName(false);
$profile->setShouldVerifyEmail(true);
Seem reasonable?
Test Plan: N/A yet, probably fatals.
Reviewers: vrana, btrahan, codeblock, chad
Reviewed By: btrahan
CC: aran, asherkin, nh, wez
Maniphest Tasks: T1536, T2394
Differential Revision: https://secure.phabricator.com/D4647
2013-06-16 10:13:49 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-08-05 14:12:43 -07:00
|
|
|
}
|