1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-15 03:05:26 +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

@@ -82,6 +82,8 @@
*/
class Tfa extends WireData implements Module, ConfigurableModule {
const userFieldName = 'tfa_type';
/**
* Name used for GET variable when TFA is active
*
@@ -99,12 +101,12 @@ class Tfa extends WireData implements Module, ConfigurableModule {
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
*
*/
protected $userFieldName = 'tfa_type';
protected $userFieldName = self::userFieldName;
/**
* Default settings
@@ -160,6 +162,7 @@ class Tfa extends WireData implements Module, ConfigurableModule {
*
*/
public function wired() {
// @todo convert to getLabel() switch and make defaults (above) blank
$this->setArray(array(
'cancelLabel' =>
$this->_('Cancel'),
@@ -499,6 +502,11 @@ class Tfa extends WireData implements Module, ConfigurableModule {
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 && $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(!$this->autoEnableSupported()) return false;
// 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 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()) {
$moduleName = $this->className();
$userModuleName = $user->get($this->userFieldName);
$userModule = $user->get($this->userFieldName);
$userModuleName = is_object($userModule) ? $userModule->className() : $userModule;
if($userModuleName === $moduleName) {
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
* 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
* @return string Returns Tfa module name or blank string if Tfa not enabled for user
* @since 3.0.160
* @todo determine if this method is really needed before moving to public API
* @return string|Tfa Returns Tfa module name, module instance (if required), or boolean false if Tfa not enabled for user
* @param bool|Tfa|string $getInstance Return Tfa module instance rather than name?
* 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) {
$moduleName = $user->get($this->userFieldName);
if(empty($moduleName)) return '';
if($moduleName === $this->className()) {
$module = $this;
} else {
$module = $this->wire()->modules->getModule($moduleName, array(
public static function getUserTfaType(User $user, $getInstance = false) {
$moduleName = '';
$module = null;
$fieldName = self::userFieldName;
$field = $user->wire()->fields->get($fieldName);
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,
'noCache' => true,
));
if(!$module) return '';
}
if(!$module || !$module instanceof Tfa) return false;
$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');
}
/**
* 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
*