Formally modularize MFA/TOTP tokens, provide a module panel for temporary tokens
Summary:
Ref T10603. We have a couple of sort of ad-hoc tokens, so start formalizing them. First up is MFA tokens.
Also adds a new config module panel for these.
Test Plan:
  - Added MFA.
  - Added MFA, intentionally fumbled the input, completed the workflow.
  - Removed MFA.
  - Viewed tokens, saw MFA sync tokens.
  - Viewed new module config panel.
{F1177014}
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T10603
Differential Revision: https://secure.phabricator.com/D15479
			
			
This commit is contained in:
		| @@ -1843,10 +1843,12 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php', |     'PhabricatorAuthSessionQuery' => 'applications/auth/query/PhabricatorAuthSessionQuery.php', | ||||||
|     'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php', |     'PhabricatorAuthSetupCheck' => 'applications/config/check/PhabricatorAuthSetupCheck.php', | ||||||
|     'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php', |     'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php', | ||||||
|  |     'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'applications/auth/factor/PhabricatorAuthTOTPKeyTemporaryTokenType.php', | ||||||
|     'PhabricatorAuthTemporaryToken' => 'applications/auth/storage/PhabricatorAuthTemporaryToken.php', |     'PhabricatorAuthTemporaryToken' => 'applications/auth/storage/PhabricatorAuthTemporaryToken.php', | ||||||
|     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php', |     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'applications/auth/garbagecollector/PhabricatorAuthTemporaryTokenGarbageCollector.php', | ||||||
|     'PhabricatorAuthTemporaryTokenQuery' => 'applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php', |     'PhabricatorAuthTemporaryTokenQuery' => 'applications/auth/query/PhabricatorAuthTemporaryTokenQuery.php', | ||||||
|     'PhabricatorAuthTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenType.php', |     'PhabricatorAuthTemporaryTokenType' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenType.php', | ||||||
|  |     'PhabricatorAuthTemporaryTokenTypeModule' => 'applications/auth/tokentype/PhabricatorAuthTemporaryTokenTypeModule.php', | ||||||
|     'PhabricatorAuthTerminateSessionController' => 'applications/auth/controller/PhabricatorAuthTerminateSessionController.php', |     'PhabricatorAuthTerminateSessionController' => 'applications/auth/controller/PhabricatorAuthTerminateSessionController.php', | ||||||
|     'PhabricatorAuthTryFactorAction' => 'applications/auth/action/PhabricatorAuthTryFactorAction.php', |     'PhabricatorAuthTryFactorAction' => 'applications/auth/action/PhabricatorAuthTryFactorAction.php', | ||||||
|     'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php', |     'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php', | ||||||
| @@ -6164,6 +6166,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorAuthSessionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck', |     'PhabricatorAuthSetupCheck' => 'PhabricatorSetupCheck', | ||||||
|     'PhabricatorAuthStartController' => 'PhabricatorAuthController', |     'PhabricatorAuthStartController' => 'PhabricatorAuthController', | ||||||
|  |     'PhabricatorAuthTOTPKeyTemporaryTokenType' => 'PhabricatorAuthTemporaryTokenType', | ||||||
|     'PhabricatorAuthTemporaryToken' => array( |     'PhabricatorAuthTemporaryToken' => array( | ||||||
|       'PhabricatorAuthDAO', |       'PhabricatorAuthDAO', | ||||||
|       'PhabricatorPolicyInterface', |       'PhabricatorPolicyInterface', | ||||||
| @@ -6171,6 +6174,7 @@ phutil_register_library_map(array( | |||||||
|     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'PhabricatorGarbageCollector', |     'PhabricatorAuthTemporaryTokenGarbageCollector' => 'PhabricatorGarbageCollector', | ||||||
|     'PhabricatorAuthTemporaryTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', |     'PhabricatorAuthTemporaryTokenQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', | ||||||
|     'PhabricatorAuthTemporaryTokenType' => 'Phobject', |     'PhabricatorAuthTemporaryTokenType' => 'Phobject', | ||||||
|  |     'PhabricatorAuthTemporaryTokenTypeModule' => 'PhabricatorConfigModule', | ||||||
|     'PhabricatorAuthTerminateSessionController' => 'PhabricatorAuthController', |     'PhabricatorAuthTerminateSessionController' => 'PhabricatorAuthController', | ||||||
|     'PhabricatorAuthTryFactorAction' => 'PhabricatorSystemAction', |     'PhabricatorAuthTryFactorAction' => 'PhabricatorSystemAction', | ||||||
|     'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController', |     'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController', | ||||||
|   | |||||||
| @@ -0,0 +1,17 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class PhabricatorAuthTOTPKeyTemporaryTokenType | ||||||
|  |   extends PhabricatorAuthTemporaryTokenType { | ||||||
|  |  | ||||||
|  |   const TOKENTYPE = 'mfa:totp:key'; | ||||||
|  |  | ||||||
|  |   public function getTokenTypeDisplayName() { | ||||||
|  |     return pht('TOTP Synchronization'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getTokenReadableTypeName( | ||||||
|  |     PhabricatorAuthTemporaryToken $token) { | ||||||
|  |     return pht('TOTP Sync Token'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -2,8 +2,6 @@ | |||||||
|  |  | ||||||
| final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | ||||||
|  |  | ||||||
|   const TEMPORARY_TOKEN_TYPE = 'mfa:totp:key'; |  | ||||||
|  |  | ||||||
|   public function getFactorKey() { |   public function getFactorKey() { | ||||||
|     return 'totp'; |     return 'totp'; | ||||||
|   } |   } | ||||||
| @@ -24,6 +22,8 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | |||||||
|     AphrontRequest $request, |     AphrontRequest $request, | ||||||
|     PhabricatorUser $user) { |     PhabricatorUser $user) { | ||||||
|  |  | ||||||
|  |     $totp_token_type = PhabricatorAuthTOTPKeyTemporaryTokenType::TOKENTYPE; | ||||||
|  |  | ||||||
|     $key = $request->getStr('totpkey'); |     $key = $request->getStr('totpkey'); | ||||||
|     if (strlen($key)) { |     if (strlen($key)) { | ||||||
|       // If the user is providing a key, make sure it's a key we generated. |       // If the user is providing a key, make sure it's a key we generated. | ||||||
| @@ -37,7 +37,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | |||||||
|       $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) |       $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) | ||||||
|         ->setViewer($user) |         ->setViewer($user) | ||||||
|         ->withTokenResources(array($user->getPHID())) |         ->withTokenResources(array($user->getPHID())) | ||||||
|         ->withTokenTypes(array(self::TEMPORARY_TOKEN_TYPE)) |         ->withTokenTypes(array($totp_token_type)) | ||||||
|         ->withExpired(false) |         ->withExpired(false) | ||||||
|         ->withTokenCodes(array(PhabricatorHash::digest($key))) |         ->withTokenCodes(array(PhabricatorHash::digest($key))) | ||||||
|         ->executeOne(); |         ->executeOne(); | ||||||
| @@ -56,7 +56,7 @@ final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | |||||||
|       $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); |       $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | ||||||
|         id(new PhabricatorAuthTemporaryToken()) |         id(new PhabricatorAuthTemporaryToken()) | ||||||
|           ->setTokenResource($user->getPHID()) |           ->setTokenResource($user->getPHID()) | ||||||
|           ->setTokenType(self::TEMPORARY_TOKEN_TYPE) |           ->setTokenType($totp_token_type) | ||||||
|           ->setTokenExpires(time() + phutil_units('1 hour in seconds')) |           ->setTokenExpires(time() + phutil_units('1 hour in seconds')) | ||||||
|           ->setTokenCode(PhabricatorHash::digest($key)) |           ->setTokenCode(PhabricatorHash::digest($key)) | ||||||
|           ->save(); |           ->save(); | ||||||
|   | |||||||
| @@ -5,6 +5,10 @@ final class PhabricatorAuthOneTimeLoginTemporaryTokenType | |||||||
|  |  | ||||||
|   const TOKENTYPE = 'login:onetime'; |   const TOKENTYPE = 'login:onetime'; | ||||||
|  |  | ||||||
|  |   public function getTokenTypeDisplayName() { | ||||||
|  |     return pht('One-Time Login'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getTokenReadableTypeName( |   public function getTokenReadableTypeName( | ||||||
|     PhabricatorAuthTemporaryToken $token) { |     PhabricatorAuthTemporaryToken $token) { | ||||||
|     return pht('One-Time Login Token'); |     return pht('One-Time Login Token'); | ||||||
|   | |||||||
| @@ -5,6 +5,10 @@ final class PhabricatorAuthPasswordResetTemporaryTokenType | |||||||
|  |  | ||||||
|   const TOKENTYPE = 'login:password'; |   const TOKENTYPE = 'login:password'; | ||||||
|  |  | ||||||
|  |   public function getTokenTypeDisplayName() { | ||||||
|  |     return pht('Password Reset'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   public function getTokenReadableTypeName( |   public function getTokenReadableTypeName( | ||||||
|     PhabricatorAuthTemporaryToken $token) { |     PhabricatorAuthTemporaryToken $token) { | ||||||
|     return pht('Password Reset Token'); |     return pht('Password Reset Token'); | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ | |||||||
| abstract class PhabricatorAuthTemporaryTokenType | abstract class PhabricatorAuthTemporaryTokenType | ||||||
|   extends Phobject { |   extends Phobject { | ||||||
|  |  | ||||||
|  |   abstract public function getTokenTypeDisplayName(); | ||||||
|   abstract public function getTokenReadableTypeName( |   abstract public function getTokenReadableTypeName( | ||||||
|     PhabricatorAuthTemporaryToken $token); |     PhabricatorAuthTemporaryToken $token); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,47 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | final class PhabricatorAuthTemporaryTokenTypeModule | ||||||
|  |   extends PhabricatorConfigModule { | ||||||
|  |  | ||||||
|  |   public function getModuleKey() { | ||||||
|  |     return 'temporarytoken'; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function getModuleName() { | ||||||
|  |     return pht('Temporary Tokens'); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   public function renderModuleStatus(AphrontRequest $request) { | ||||||
|  |     $viewer = $request->getViewer(); | ||||||
|  |  | ||||||
|  |     $types = PhabricatorAuthTemporaryTokenType::getAllTypes(); | ||||||
|  |  | ||||||
|  |     $rows = array(); | ||||||
|  |     foreach ($types as $type) { | ||||||
|  |       $rows[] = array( | ||||||
|  |         get_class($type), | ||||||
|  |         $type->getTokenTypeConstant(), | ||||||
|  |         $type->getTokenTypeDisplayName(), | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     $table = id(new AphrontTableView($rows)) | ||||||
|  |       ->setHeaders( | ||||||
|  |         array( | ||||||
|  |           pht('Class'), | ||||||
|  |           pht('Key'), | ||||||
|  |           pht('Name'), | ||||||
|  |         )) | ||||||
|  |       ->setColumnClasses( | ||||||
|  |         array( | ||||||
|  |           null, | ||||||
|  |           null, | ||||||
|  |           'wide pri', | ||||||
|  |         )); | ||||||
|  |  | ||||||
|  |     return id(new PHUIObjectBoxView()) | ||||||
|  |       ->setHeaderText(pht('Temporary Token Types')) | ||||||
|  |       ->setTable($table); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 epriestley
					epriestley