mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 16:22:43 +01:00
Add a skeleton for configurable MFA provider types
Summary: Ref T13222. Ref T13231. See PHI912. I'm planning to turn MFA providers into concrete objects, so you can disable and configure them. Currently, we only support TOTP, which doesn't require any configuration, but other provider types (like Duo or Yubikey OTP) do require some configuration (server URIs, API keys, etc). TOTP //could// also have some configuration, like "bits of entropy" or "allowed window size" or whatever, if we want. Add concrete objects for this and standard transaction / policy / query support. These objects don't do anything interesting yet and don't actually interact with MFA, this is just skeleton code for now. Test Plan: {F6090444} {F6090445} Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13231, T13222 Differential Revision: https://secure.phabricator.com/D19935
This commit is contained in:
parent
b98d46ce7d
commit
a62f334d95
23 changed files with 871 additions and 40 deletions
9
resources/sql/autopatches/20181228.auth.01.provider.sql
Normal file
9
resources/sql/autopatches/20181228.auth.01.provider.sql
Normal file
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE {$NAMESPACE}_auth.auth_factorprovider (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
providerFactorKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
19
resources/sql/autopatches/20181228.auth.02.xaction.sql
Normal file
19
resources/sql/autopatches/20181228.auth.02.xaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_auth.auth_factorprovidertransaction (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
authorPHID VARBINARY(64) NOT NULL,
|
||||
objectPHID VARBINARY(64) NOT NULL,
|
||||
viewPolicy VARBINARY(64) NOT NULL,
|
||||
editPolicy VARBINARY(64) NOT NULL,
|
||||
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL,
|
||||
oldValue LONGTEXT NOT NULL,
|
||||
newValue LONGTEXT NOT NULL,
|
||||
contentSource LONGTEXT NOT NULL,
|
||||
metadata LONGTEXT NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (`phid`),
|
||||
KEY `key_object` (`objectPHID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20181228.auth.03.name.sql
Normal file
2
resources/sql/autopatches/20181228.auth.03.name.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_auth.auth_factorprovider
|
||||
ADD name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -2190,6 +2190,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthAccountView' => 'applications/auth/view/PhabricatorAuthAccountView.php',
|
||||
'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php',
|
||||
'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
|
||||
'PhabricatorAuthAuthFactorProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorProviderPHIDType.php',
|
||||
'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php',
|
||||
'PhabricatorAuthCSRFEngine' => 'applications/auth/engine/PhabricatorAuthCSRFEngine.php',
|
||||
'PhabricatorAuthChallenge' => 'applications/auth/storage/PhabricatorAuthChallenge.php',
|
||||
|
@ -2207,6 +2208,18 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthEditController' => 'applications/auth/controller/config/PhabricatorAuthEditController.php',
|
||||
'PhabricatorAuthFactor' => 'applications/auth/factor/PhabricatorAuthFactor.php',
|
||||
'PhabricatorAuthFactorConfig' => 'applications/auth/storage/PhabricatorAuthFactorConfig.php',
|
||||
'PhabricatorAuthFactorProvider' => 'applications/auth/storage/PhabricatorAuthFactorProvider.php',
|
||||
'PhabricatorAuthFactorProviderController' => 'applications/auth/controller/mfa/PhabricatorAuthFactorProviderController.php',
|
||||
'PhabricatorAuthFactorProviderEditController' => 'applications/auth/controller/mfa/PhabricatorAuthFactorProviderEditController.php',
|
||||
'PhabricatorAuthFactorProviderEditEngine' => 'applications/auth/editor/PhabricatorAuthFactorProviderEditEngine.php',
|
||||
'PhabricatorAuthFactorProviderEditor' => 'applications/auth/editor/PhabricatorAuthFactorProviderEditor.php',
|
||||
'PhabricatorAuthFactorProviderListController' => 'applications/auth/controller/mfa/PhabricatorAuthFactorProviderListController.php',
|
||||
'PhabricatorAuthFactorProviderNameTransaction' => 'applications/auth/xaction/PhabricatorAuthFactorProviderNameTransaction.php',
|
||||
'PhabricatorAuthFactorProviderQuery' => 'applications/auth/query/PhabricatorAuthFactorProviderQuery.php',
|
||||
'PhabricatorAuthFactorProviderTransaction' => 'applications/auth/storage/PhabricatorAuthFactorProviderTransaction.php',
|
||||
'PhabricatorAuthFactorProviderTransactionQuery' => 'applications/auth/query/PhabricatorAuthFactorProviderTransactionQuery.php',
|
||||
'PhabricatorAuthFactorProviderTransactionType' => 'applications/auth/xaction/PhabricatorAuthFactorProviderTransactionType.php',
|
||||
'PhabricatorAuthFactorProviderViewController' => 'applications/auth/controller/mfa/PhabricatorAuthFactorProviderViewController.php',
|
||||
'PhabricatorAuthFactorResult' => 'applications/auth/factor/PhabricatorAuthFactorResult.php',
|
||||
'PhabricatorAuthFactorTestCase' => 'applications/auth/factor/__tests__/PhabricatorAuthFactorTestCase.php',
|
||||
'PhabricatorAuthFinishController' => 'applications/auth/controller/PhabricatorAuthFinishController.php',
|
||||
|
@ -2277,6 +2290,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthProviderConfigQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigQuery.php',
|
||||
'PhabricatorAuthProviderConfigTransaction' => 'applications/auth/storage/PhabricatorAuthProviderConfigTransaction.php',
|
||||
'PhabricatorAuthProviderConfigTransactionQuery' => 'applications/auth/query/PhabricatorAuthProviderConfigTransactionQuery.php',
|
||||
'PhabricatorAuthProviderController' => 'applications/auth/controller/config/PhabricatorAuthProviderController.php',
|
||||
'PhabricatorAuthProvidersGuidanceContext' => 'applications/auth/guidance/PhabricatorAuthProvidersGuidanceContext.php',
|
||||
'PhabricatorAuthProvidersGuidanceEngineExtension' => 'applications/auth/guidance/PhabricatorAuthProvidersGuidanceEngineExtension.php',
|
||||
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php',
|
||||
|
@ -7835,6 +7849,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthAccountView' => 'AphrontView',
|
||||
'PhabricatorAuthApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthAuthFactorProviderPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthCSRFEngine' => 'Phobject',
|
||||
'PhabricatorAuthChallenge' => array(
|
||||
|
@ -7855,6 +7870,23 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthEditController' => 'PhabricatorAuthProviderConfigController',
|
||||
'PhabricatorAuthFactor' => 'Phobject',
|
||||
'PhabricatorAuthFactorConfig' => 'PhabricatorAuthDAO',
|
||||
'PhabricatorAuthFactorProvider' => array(
|
||||
'PhabricatorAuthDAO',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorExtendedPolicyInterface',
|
||||
),
|
||||
'PhabricatorAuthFactorProviderController' => 'PhabricatorAuthProviderController',
|
||||
'PhabricatorAuthFactorProviderEditController' => 'PhabricatorAuthFactorProviderController',
|
||||
'PhabricatorAuthFactorProviderEditEngine' => 'PhabricatorEditEngine',
|
||||
'PhabricatorAuthFactorProviderEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorAuthFactorProviderListController' => 'PhabricatorAuthProviderController',
|
||||
'PhabricatorAuthFactorProviderNameTransaction' => 'PhabricatorAuthFactorProviderTransactionType',
|
||||
'PhabricatorAuthFactorProviderQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthFactorProviderTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorAuthFactorProviderTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorAuthFactorProviderTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorAuthFactorProviderViewController' => 'PhabricatorAuthFactorProviderController',
|
||||
'PhabricatorAuthFactorResult' => 'Phobject',
|
||||
'PhabricatorAuthFactorTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorAuthFinishController' => 'PhabricatorAuthController',
|
||||
|
@ -7931,11 +7963,12 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionInterface',
|
||||
'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorAuthProviderConfigController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthProviderConfigController' => 'PhabricatorAuthProviderController',
|
||||
'PhabricatorAuthProviderConfigEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'PhabricatorAuthProviderConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorAuthProviderConfigTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'PhabricatorAuthProviderConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorAuthProviderController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthProvidersGuidanceContext' => 'PhabricatorGuidanceContext',
|
||||
'PhabricatorAuthProvidersGuidanceEngineExtension' => 'PhabricatorGuidanceEngineExtension',
|
||||
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
|
||||
|
|
|
@ -85,6 +85,15 @@ final class PhabricatorAuthApplication extends PhabricatorApplication {
|
|||
'view/(?P<id>\d+)/' => 'PhabricatorAuthSSHKeyViewController',
|
||||
),
|
||||
'password/' => 'PhabricatorAuthSetPasswordController',
|
||||
|
||||
'mfa/' => array(
|
||||
$this->getQueryRoutePattern() =>
|
||||
'PhabricatorAuthFactorProviderListController',
|
||||
$this->getEditRoutePattern('edit/') =>
|
||||
'PhabricatorAuthFactorProviderEditController',
|
||||
'(?P<id>[1-9]\d*)/' =>
|
||||
'PhabricatorAuthFactorProviderViewController',
|
||||
),
|
||||
),
|
||||
|
||||
'/oauth/(?P<provider>\w+)/login/'
|
||||
|
|
|
@ -91,7 +91,7 @@ final class PhabricatorAuthListController
|
|||
pht('Add Authentication Provider'))));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs();
|
||||
$crumbs->addTextCrumb(pht('Auth Providers'));
|
||||
$crumbs->addTextCrumb(pht('Login and Registration'));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$guidance_context = new PhabricatorAuthProvidersGuidanceContext();
|
||||
|
@ -102,12 +102,12 @@ final class PhabricatorAuthListController
|
|||
->newInfoView();
|
||||
|
||||
$button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)
|
||||
->setHref($this->getApplicationURI('config/new/'))
|
||||
->setIcon('fa-plus')
|
||||
->setDisabled(!$can_manage)
|
||||
->setText(pht('Add Provider'));
|
||||
->setTag('a')
|
||||
->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)
|
||||
->setHref($this->getApplicationURI('config/new/'))
|
||||
->setIcon('fa-plus')
|
||||
->setDisabled(!$can_manage)
|
||||
->setText(pht('Add Provider'));
|
||||
|
||||
$list->setFlush(true);
|
||||
$list = id(new PHUIObjectBoxView())
|
||||
|
@ -115,7 +115,7 @@ final class PhabricatorAuthListController
|
|||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($list);
|
||||
|
||||
$title = pht('Auth Providers');
|
||||
$title = pht('Login and Registration Providers');
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setHeaderIcon('fa-key')
|
||||
|
@ -128,10 +128,15 @@ final class PhabricatorAuthListController
|
|||
$list,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
$nav = $this->newNavigation()
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
|
||||
$nav->selectFilter('login');
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->appendChild($nav);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,32 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorAuthProviderConfigController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
protected function buildSideNavView($for_app = false) {
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
if ($for_app) {
|
||||
$nav->addLabel(pht('Create'));
|
||||
$nav->addFilter('',
|
||||
pht('Add Authentication Provider'),
|
||||
$this->getApplicationURI('/config/new/'));
|
||||
}
|
||||
return $nav;
|
||||
}
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->buildSideNavView($for_app = true)->getMenu();
|
||||
}
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$can_create = $this->hasApplicationCapability(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
}
|
||||
extends PhabricatorAuthProviderController {}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorAuthProviderController
|
||||
extends PhabricatorAuthController {
|
||||
|
||||
protected function newNavigation() {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$nav = id(new AphrontSideNavFilterView())
|
||||
->setBaseURI(new PhutilURI($this->getApplicationURI()))
|
||||
->setViewer($viewer);
|
||||
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Authentication'))
|
||||
->setType(PHUIListItemView::TYPE_LABEL));
|
||||
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setKey('login')
|
||||
->setName(pht('Login and Registration'))
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref($this->getApplicationURI('/'))
|
||||
->setIcon('fa-key'));
|
||||
|
||||
$nav->addMenuItem(
|
||||
id(new PHUIListItemView())
|
||||
->setKey('mfa')
|
||||
->setName(pht('Multi-Factor'))
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref($this->getApplicationURI('mfa/'))
|
||||
->setIcon('fa-mobile'));
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->newNavigation()->getMenu();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorAuthFactorProviderController
|
||||
extends PhabricatorAuthProviderController {
|
||||
|
||||
protected function buildApplicationCrumbs() {
|
||||
return parent::buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Multi-Factor'), $this->getApplicationURI('mfa/'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderEditController
|
||||
extends PhabricatorAuthFactorProviderController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$this->requireApplicationCapability(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
|
||||
$engine = id(new PhabricatorAuthFactorProviderEditEngine())
|
||||
->setController($this);
|
||||
|
||||
$id = $request->getURIData('id');
|
||||
if (!$id) {
|
||||
$factor_key = $request->getStr('providerFactorKey');
|
||||
|
||||
$map = PhabricatorAuthFactor::getAllFactors();
|
||||
$factor = idx($map, $factor_key);
|
||||
if (!$factor) {
|
||||
return $this->buildFactorSelectionResponse();
|
||||
}
|
||||
|
||||
$engine
|
||||
->addContextParameter('providerFactorKey', $factor_key)
|
||||
->setProviderFactor($factor);
|
||||
}
|
||||
|
||||
return $engine->buildResponse();
|
||||
}
|
||||
|
||||
private function buildFactorSelectionResponse() {
|
||||
$request = $this->getRequest();
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$cancel_uri = $this->getApplicationURI('mfa/');
|
||||
|
||||
$factors = PhabricatorAuthFactor::getAllFactors();
|
||||
|
||||
$menu = id(new PHUIObjectItemListView())
|
||||
->setUser($viewer)
|
||||
->setBig(true)
|
||||
->setFlush(true);
|
||||
|
||||
foreach ($factors as $factor_key => $factor) {
|
||||
$factor_uri = id(new PhutilURI('/mfa/edit/'))
|
||||
->setQueryParam('providerFactorKey', $factor_key);
|
||||
$factor_uri = $this->getApplicationURI($factor_uri);
|
||||
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setHeader($factor->getFactorName())
|
||||
->setHref($factor_uri)
|
||||
->setClickable(true)
|
||||
->setImageIcon($factor->newIconView())
|
||||
->addAttribute($factor->getFactorCreateHelp());
|
||||
|
||||
$menu->addItem($item);
|
||||
}
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Choose Provider Type'))
|
||||
->appendChild($menu)
|
||||
->addCancelButton($cancel_uri);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderListController
|
||||
extends PhabricatorAuthProviderController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$can_manage = $this->hasApplicationCapability(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
|
||||
$providers = id(new PhabricatorAuthFactorProviderQuery())
|
||||
->setViewer($viewer)
|
||||
->execute();
|
||||
|
||||
$list = new PHUIObjectItemListView();
|
||||
foreach ($providers as $provider) {
|
||||
$item = id(new PHUIObjectItemView())
|
||||
->setObjectName($provider->getObjectName())
|
||||
->setHeader($provider->getDisplayName())
|
||||
->setHref($provider->getURI());
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
$list->setNoDataString(
|
||||
pht('You have not configured any multi-factor providers yet.'));
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb(pht('Multi-Factor'))
|
||||
->setBorder(true);
|
||||
|
||||
$button = id(new PHUIButtonView())
|
||||
->setTag('a')
|
||||
->setButtonType(PHUIButtonView::BUTTONTYPE_SIMPLE)
|
||||
->setHref($this->getApplicationURI('mfa/edit/'))
|
||||
->setIcon('fa-plus')
|
||||
->setDisabled(!$can_manage)
|
||||
->setWorkflow(true)
|
||||
->setText(pht('Add MFA Provider'));
|
||||
|
||||
$list->setFlush(true);
|
||||
$list = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('MFA Providers'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($list);
|
||||
|
||||
$title = pht('MFA Providers');
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($title)
|
||||
->setHeaderIcon('fa-mobile')
|
||||
->addActionLink($button);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(
|
||||
array(
|
||||
$list,
|
||||
));
|
||||
|
||||
$nav = $this->newNavigation()
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
|
||||
$nav->selectFilter('mfa');
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($title)
|
||||
->appendChild($nav);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderViewController
|
||||
extends PhabricatorAuthFactorProviderController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$this->requireApplicationCapability(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
|
||||
$provider = id(new PhabricatorAuthFactorProviderQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($request->getURIData('id')))
|
||||
->executeOne();
|
||||
if (!$provider) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($provider->getObjectName())
|
||||
->setBorder(true);
|
||||
|
||||
$header = $this->buildHeaderView($provider);
|
||||
$properties = $this->buildPropertiesView($provider);
|
||||
$curtain = $this->buildCurtain($provider);
|
||||
|
||||
|
||||
$timeline = $this->buildTransactionTimeline(
|
||||
$provider,
|
||||
new PhabricatorAuthFactorProviderTransactionQuery());
|
||||
$timeline->setShouldTerminate(true);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setCurtain($curtain)
|
||||
->setMainColumn(
|
||||
array(
|
||||
$timeline,
|
||||
))
|
||||
->addPropertySection(pht('Details'), $properties);
|
||||
|
||||
return $this->newPage()
|
||||
->setTitle($provider->getDisplayName())
|
||||
->setCrumbs($crumbs)
|
||||
->setPageObjectPHIDs(
|
||||
array(
|
||||
$provider->getPHID(),
|
||||
))
|
||||
->appendChild($view);
|
||||
}
|
||||
|
||||
private function buildHeaderView(PhabricatorAuthFactorProvider $provider) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($provider->getDisplayName())
|
||||
->setPolicyObject($provider);
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildPropertiesView(
|
||||
PhabricatorAuthFactorProvider $provider) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$view = id(new PHUIPropertyListView())
|
||||
->setViewer($viewer);
|
||||
|
||||
$view->addProperty(
|
||||
pht('Factor Type'),
|
||||
$provider->getFactor()->getFactorName());
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function buildCurtain(PhabricatorAuthFactorProvider $provider) {
|
||||
$viewer = $this->getViewer();
|
||||
$id = $provider->getID();
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$provider,
|
||||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
|
||||
$curtain = $this->newCurtainView($provider);
|
||||
|
||||
$curtain->addAction(
|
||||
id(new PhabricatorActionView())
|
||||
->setName(pht('Edit MFA Provider'))
|
||||
->setIcon('fa-pencil')
|
||||
->setHref($this->getApplicationURI("mfa/edit/{$id}/"))
|
||||
->setDisabled(!$can_edit)
|
||||
->setWorkflow(!$can_edit));
|
||||
|
||||
return $curtain;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderEditEngine
|
||||
extends PhabricatorEditEngine {
|
||||
|
||||
private $providerFactor;
|
||||
|
||||
const ENGINECONST = 'auth.factor.provider';
|
||||
|
||||
public function isEngineConfigurable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getEngineName() {
|
||||
return pht('MFA Providers');
|
||||
}
|
||||
|
||||
public function getSummaryHeader() {
|
||||
return pht('Edit MFA Providers');
|
||||
}
|
||||
|
||||
public function getSummaryText() {
|
||||
return pht('This engine is used to edit MFA providers.');
|
||||
}
|
||||
|
||||
public function getEngineApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
public function setProviderFactor(PhabricatorAuthFactor $factor) {
|
||||
$this->providerFactor = $factor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProviderFactor() {
|
||||
return $this->providerFactor;
|
||||
}
|
||||
|
||||
protected function newEditableObject() {
|
||||
$factor = $this->getProviderFactor();
|
||||
if ($factor) {
|
||||
$provider = PhabricatorAuthFactorProvider::initializeNewProvider($factor);
|
||||
} else {
|
||||
$provider = new PhabricatorAuthFactorProvider();
|
||||
}
|
||||
|
||||
return $provider;
|
||||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new PhabricatorAuthFactorProviderQuery();
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
return pht('Create MFA Provider');
|
||||
}
|
||||
|
||||
protected function getObjectCreateButtonText($object) {
|
||||
return pht('Create MFA Provider');
|
||||
}
|
||||
|
||||
protected function getObjectEditTitleText($object) {
|
||||
return pht('Edit MFA Provider');
|
||||
}
|
||||
|
||||
protected function getObjectEditShortText($object) {
|
||||
return $object->getObjectName();
|
||||
}
|
||||
|
||||
protected function getObjectCreateShortText() {
|
||||
return pht('Create MFA Provider');
|
||||
}
|
||||
|
||||
protected function getObjectName() {
|
||||
return pht('MFA Provider');
|
||||
}
|
||||
|
||||
protected function getEditorURI() {
|
||||
return '/auth/mfa/edit/';
|
||||
}
|
||||
|
||||
protected function getObjectCreateCancelURI($object) {
|
||||
return '/auth/mfa/';
|
||||
}
|
||||
|
||||
protected function getObjectViewURI($object) {
|
||||
return $object->getURI();
|
||||
}
|
||||
|
||||
protected function getCreateNewObjectPolicy() {
|
||||
return $this->getApplication()->getPolicy(
|
||||
AuthManageProvidersCapability::CAPABILITY);
|
||||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$factor_name = $object->getFactor()->getFactorName();
|
||||
|
||||
return array(
|
||||
id(new PhabricatorStaticEditField())
|
||||
->setKey('displayType')
|
||||
->setLabel(pht('Factor Type'))
|
||||
->setDescription(pht('Type of the MFA provider.'))
|
||||
->setValue($factor_name),
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
->setTransactionType(
|
||||
PhabricatorAuthFactorProviderNameTransaction::TRANSACTIONTYPE)
|
||||
->setLabel(pht('Name'))
|
||||
->setDescription(pht('Display name for the MFA provider.'))
|
||||
->setValue($object->getName())
|
||||
->setPlaceholder($factor_name),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderEditor
|
||||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
public function getEditorObjectsDescription() {
|
||||
return pht('MFA Providers');
|
||||
}
|
||||
|
||||
public function getCreateObjectTitle($author, $object) {
|
||||
return pht('%s created this MFA provider.', $author);
|
||||
}
|
||||
|
||||
public function getCreateObjectTitleForFeed($author, $object) {
|
||||
return pht('%s created %s.', $author, $object);
|
||||
}
|
||||
|
||||
}
|
|
@ -4,6 +4,7 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
|
||||
abstract public function getFactorName();
|
||||
abstract public function getFactorKey();
|
||||
abstract public function getFactorCreateHelp();
|
||||
abstract public function getFactorDescription();
|
||||
abstract public function processAddFactorForm(
|
||||
AphrontFormView $form,
|
||||
|
@ -39,6 +40,11 @@ abstract class PhabricatorAuthFactor extends Phobject {
|
|||
return new PhabricatorAuthFactorResult();
|
||||
}
|
||||
|
||||
public function newIconView() {
|
||||
return id(new PHUIIconView())
|
||||
->setIcon('fa-mobile');
|
||||
}
|
||||
|
||||
protected function newChallenge(
|
||||
PhabricatorAuthFactorConfig $config,
|
||||
PhabricatorUser $viewer) {
|
||||
|
|
|
@ -12,6 +12,12 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor {
|
|||
return pht('Mobile Phone App (TOTP)');
|
||||
}
|
||||
|
||||
public function getFactorCreateHelp() {
|
||||
return pht(
|
||||
'Allow users to attach a mobile authenticator application (like '.
|
||||
'Google Authenticator) to their account.');
|
||||
}
|
||||
|
||||
public function getFactorDescription() {
|
||||
return pht(
|
||||
'Attach a mobile authenticator application (like Authy '.
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthAuthFactorProviderPHIDType
|
||||
extends PhabricatorPHIDType {
|
||||
|
||||
const TYPECONST = 'FPRV';
|
||||
|
||||
public function getTypeName() {
|
||||
return pht('MFA Provider');
|
||||
}
|
||||
|
||||
public function newObject() {
|
||||
return new PhabricatorAuthFactorProvider();
|
||||
}
|
||||
|
||||
public function getPHIDTypeApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
protected function buildQueryForObjects(
|
||||
PhabricatorObjectQuery $query,
|
||||
array $phids) {
|
||||
|
||||
return id(new PhabricatorAuthFactorProviderQuery())
|
||||
->withPHIDs($phids);
|
||||
}
|
||||
|
||||
public function loadHandles(
|
||||
PhabricatorHandleQuery $query,
|
||||
array $handles,
|
||||
array $objects) {
|
||||
|
||||
foreach ($handles as $phid => $handle) {
|
||||
$provider = $objects[$phid];
|
||||
|
||||
$handle->setURI($provider->getURI());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $ids;
|
||||
private $phids;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
public function newResultObject() {
|
||||
return new PhabricatorAuthFactorProvider();
|
||||
}
|
||||
|
||||
protected function loadPage() {
|
||||
return $this->loadStandardPage($this->newResultObject());
|
||||
}
|
||||
|
||||
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
||||
$where = parent::buildWhereClauseParts($conn);
|
||||
|
||||
if ($this->ids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
protected function willFilterPage(array $providers) {
|
||||
$map = PhabricatorAuthFactor::getAllFactors();
|
||||
foreach ($providers as $key => $provider) {
|
||||
$factor_key = $provider->getProviderFactorKey();
|
||||
$factor = idx($map, $factor_key);
|
||||
|
||||
if (!$factor) {
|
||||
unset($providers[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$provider->attachFactor($factor);
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
public function getQueryApplicationClass() {
|
||||
return 'PhabricatorAuthApplication';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhabricatorAuthFactorProviderTransaction();
|
||||
}
|
||||
|
||||
}
|
134
src/applications/auth/storage/PhabricatorAuthFactorProvider.php
Normal file
134
src/applications/auth/storage/PhabricatorAuthFactorProvider.php
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProvider
|
||||
extends PhabricatorAuthDAO
|
||||
implements
|
||||
PhabricatorApplicationTransactionInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorExtendedPolicyInterface {
|
||||
|
||||
protected $providerFactorKey;
|
||||
protected $name;
|
||||
protected $status;
|
||||
protected $properties = array();
|
||||
|
||||
private $factor = self::ATTACHABLE;
|
||||
|
||||
const STATUS_ACTIVE = 'active';
|
||||
const STATUS_DEPRECATED = 'deprecated';
|
||||
const STATUS_DISABLED = 'disabled';
|
||||
|
||||
public static function initializeNewProvider(PhabricatorAuthFactor $factor) {
|
||||
return id(new self())
|
||||
->setProviderFactorKey($factor->getFactorKey())
|
||||
->attachFactor($factor)
|
||||
->setStatus(self::STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_SERIALIZATION => array(
|
||||
'properties' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'providerFactorKey' => 'text64',
|
||||
'name' => 'text255',
|
||||
'status' => 'text32',
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function getPHIDType() {
|
||||
return PhabricatorAuthAuthFactorProviderPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/auth/mfa/'.$this->getID().'/';
|
||||
}
|
||||
|
||||
public function getObjectName() {
|
||||
return pht('MFA Provider %d', $this->getID());
|
||||
}
|
||||
|
||||
public function getAuthFactorProviderProperty($key, $default = null) {
|
||||
return idx($this->properties, $key, $default);
|
||||
}
|
||||
|
||||
public function setAuthFactorProviderProperty($key, $value) {
|
||||
$this->properties[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function attachFactor(PhabricatorAuthFactor $factor) {
|
||||
$this->factor = $factor;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFactor() {
|
||||
return $this->assertAttached($this->factor);
|
||||
}
|
||||
|
||||
public function getDisplayName() {
|
||||
$name = $this->getName();
|
||||
if (strlen($name)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $this->getFactor()->getFactorName();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new PhabricatorAuthFactorProviderEditor();
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorAuthFactorProviderTransaction();
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::getMostOpenPolicy();
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
|
||||
|
||||
|
||||
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
|
||||
$extended = array();
|
||||
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
break;
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
$extended[] = array(
|
||||
new PhabricatorAuthApplication(),
|
||||
AuthManageProvidersCapability::CAPABILITY,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $extended;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderTransaction
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'auth';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return PhabricatorAuthAuthFactorProviderPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'PhabricatorAuthFactorProviderTransactionType';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthFactorProviderNameTransaction
|
||||
extends PhabricatorAuthFactorProviderTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'name';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getName();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setName($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
if (!strlen($old)) {
|
||||
return pht(
|
||||
'%s named this provider %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderNewValue());
|
||||
} else if (!strlen($new)) {
|
||||
return pht(
|
||||
'%s removed the name (%s) of this provider.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue());
|
||||
} else {
|
||||
return pht(
|
||||
'%s renamed this provider from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderOldValue(),
|
||||
$this->renderNewValue());
|
||||
}
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
$max_length = $object->getColumnMaximumByteLength('name');
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_value = $xaction->getNewValue();
|
||||
$new_length = strlen($new_value);
|
||||
if ($new_length > $max_length) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Provider names can not be longer than %s characters.',
|
||||
new PhutilNumber($max_length)),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function getTransactionTypeForConduit($xaction) {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit($xaction, $data) {
|
||||
return array(
|
||||
'old' => $xaction->getOldValue(),
|
||||
'new' => $xaction->getNewValue(),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorAuthFactorProviderTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
Loading…
Reference in a new issue