1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-16 03:34:33 +02:00

Add User::hasTfa() method to quickly identify if user has two-factor authentication enabled. Also added supporting code in Tfa class.

This commit is contained in:
Ryan Cramer
2020-07-10 12:44:16 -04:00
parent e806cdb044
commit 8e0f2ed721
2 changed files with 84 additions and 16 deletions

View File

@@ -81,6 +81,8 @@
* *
*/ */
class Tfa extends WireData implements Module, ConfigurableModule { class Tfa extends WireData implements Module, ConfigurableModule {
const userFieldName = 'tfa_type';
/** /**
* Name used for GET variable when TFA is active * Name used for GET variable when TFA is active
@@ -99,12 +101,12 @@ class Tfa extends WireData implements Module, ConfigurableModule {
protected $authCodeForm = null; protected $authCodeForm = null;
/** /**
* Prefix for field names on user template to store TFA data * Name of field carrying Tfa type selection on user template
* *
* @var string * @var string
* *
*/ */
protected $userFieldName = 'tfa_type'; protected $userFieldName = self::userFieldName;
/** /**
* Default settings * Default settings
@@ -160,6 +162,7 @@ class Tfa extends WireData implements Module, ConfigurableModule {
* *
*/ */
public function wired() { public function wired() {
// @todo convert to getLabel() switch and make defaults (above) blank
$this->setArray(array( $this->setArray(array(
'cancelLabel' => 'cancelLabel' =>
$this->_('Cancel'), $this->_('Cancel'),
@@ -499,6 +502,11 @@ class Tfa extends WireData implements Module, ConfigurableModule {
if($user) $module = $user->get($this->userFieldName); if($user) $module = $user->get($this->userFieldName);
} }
if($module && is_string($module)) {
// if field returned module name rather than instance (field settings)
$module = $this->wire()->modules->getModule($module);
}
if($module && !$module instanceof Tfa) $module = null; if($module && !$module instanceof Tfa) $module = null;
if(!$module && $user && $this->autoType) { if(!$module && $user && $this->autoType) {
@@ -923,7 +931,8 @@ class Tfa extends WireData implements Module, ConfigurableModule {
// if it doesn't support it without user, then exit now // if it doesn't support it without user, then exit now
if(!$this->autoEnableSupported()) return false; if(!$this->autoEnableSupported()) return false;
// if support is present and user already has it enabled, we can assume support // if support is present and user already has it enabled, we can assume support
$userModuleName = $this->get($this->userFieldName); $userModule = $this->get($this->userFieldName);
$userModuleName = is_object($userModule) ? $userModule->className() : $userModule;
if($userModuleName === $this->className()) return true; if($userModuleName === $this->className()) return true;
// if user has some other Tfa module present, then not supported right now // if user has some other Tfa module present, then not supported right now
} }
@@ -948,7 +957,8 @@ class Tfa extends WireData implements Module, ConfigurableModule {
public function autoEnableUser(User $user, array $settings = array()) { public function autoEnableUser(User $user, array $settings = array()) {
$moduleName = $this->className(); $moduleName = $this->className();
$userModuleName = $user->get($this->userFieldName); $userModule = $user->get($this->userFieldName);
$userModuleName = is_object($userModule) ? $userModule->className() : $userModule;
if($userModuleName === $moduleName) { if($userModuleName === $moduleName) {
return; // already enabled for user return; // already enabled for user
@@ -1126,26 +1136,60 @@ class Tfa extends WireData implements Module, ConfigurableModule {
* This method is okay to call from a base Tfa class instance, and is useful for determining * This method is okay to call from a base Tfa class instance, and is useful for determining
* whether the user has Tfa enabled. * whether the user has Tfa enabled.
* *
* In many cases it is preferable to accessing $user->tfa_type directly because is doesnt
* trigger Tfa module to load and attach hooks. In addition, it accounts for the case where
* user has selected a Tfa module but has not yet configured it.
*
* #pw-internal This method may move elsewhere, so call $user->hasTfa() instead.
*
* @param User $user * @param User $user
* @return string Returns Tfa module name or blank string if Tfa not enabled for user * @return string|Tfa Returns Tfa module name, module instance (if required), or boolean false if Tfa not enabled for user
* @since 3.0.160 * @param bool|Tfa|string $getInstance Return Tfa module instance rather than name?
* @todo determine if this method is really needed before moving to public API * Note: returned instance may not be fully initialized. If you need fully initialized, use return value of
* $user->tfa_type after getting a non-empty return value from this method.
* @since 3.0.162
* *
*/ */
protected function getUserTfaType(User $user) { public static function getUserTfaType(User $user, $getInstance = false) {
$moduleName = $user->get($this->userFieldName);
if(empty($moduleName)) return ''; $moduleName = '';
if($moduleName === $this->className()) { $module = null;
$module = $this; $fieldName = self::userFieldName;
} else { $field = $user->wire()->fields->get($fieldName);
$module = $this->wire()->modules->getModule($moduleName, array(
if(!$field || !$field->type) {
// Tfa not yet enabled in system
return false;
}
if($user->isLoaded($fieldName)) {
$module = $user->get($fieldName);
if(empty($module)) return false;
if(!is_object($module)) list($moduleName, $module) = array($module, null);
}
if(!$module && !$moduleName) {
/** @var FieldtypeModule $fieldtype */
$fieldtype = $field->type;
$moduleID = $fieldtype->___loadPageField($user, $field);
if(!$moduleID) return false;
$moduleName = $user->wire()->modules->getModuleClass($moduleID);
if(!$moduleName) return false;
}
if(!$module) {
$module = $user->wire()->modules->getModule($moduleName, array(
'noInit' => true, 'noInit' => true,
'noCache' => true, 'noCache' => true,
)); ));
if(!$module) return '';
} }
if(!$module || !$module instanceof Tfa) return false;
$settings = $module->getUserSettings($user); $settings = $module->getUserSettings($user);
return $module->enabledForUser($user, $settings) ? $moduleName : ''; if(!$module->enabledForUser($user, $settings)) return false;
return $getInstance ? $module : $moduleName;
} }
/** /**

View File

@@ -500,6 +500,30 @@ class User extends Page {
return $this->wire('users'); return $this->wire('users');
} }
/**
* Does user have two-factor authentication (Tfa) enabled? (and what type?)
*
* - Returns boolean false if not enabled.
* - Returns string with Tfa module name (string) if enabled.
* - When `$getInstance` argument is true, returns Tfa module instance rather than module name.
*
* The benefit of using this method is that it can identify if Tfa is enabled without fully
* initializing a Tfa module that would attach hooks, etc. So when you only need to know if
* Tfa is enabled for a user, this method is more efficient than accessing `$user->tfa_type`.
*
* When using `$getInstance` to return module instance, note that the module instance might not
* be initialized (hooks not added, etc.). To retrieve an initialized instance, you can get it
* from `$user->tfa_type` rather than calling this method.
*
* @param bool $getInstance Get Tfa module instance when available? (default=false)
* @return bool|string|Tfa
* @since 3.0.162
*
*/
public function hasTfa($getInstance = false) {
return Tfa::getUserTfaType($this, $getInstance);
}
/** /**
* Hook called when field has changed * Hook called when field has changed
* *