Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+16 -1
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA\Authy;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -22,9 +22,24 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Exception extends \DomainException
{
/**
* @brief Token reused
*/
const TOKEN_REUSED = 60019;
/**
* @brief Token invalid
*/
const TOKEN_INVALID = 60020;
/**
* @brief User invalid
*/
const USER_INVALID = 60027;
/**
* @brief Phone number invalid
*/
const PHONE_NUMBER_INVALID = 60033;
/**
+21 -20
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA\Authy;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -86,7 +86,7 @@ class _Handler extends \IPS\MFA\MFAHandler
{
/* Asking for a text or call instead? */
$availableMethods = explode( ',', \IPS\Settings::i()->authy_setup );
if ( isset( \IPS\Request::i()->authy_method ) and $mfaDetails['authy']['setupMethod'] == 'authy' and in_array( \IPS\Request::i()->authy_method, $availableMethods ) )
if ( isset( \IPS\Request::i()->authy_method ) and $mfaDetails['authy']['setupMethod'] == 'authy' and \in_array( \IPS\Request::i()->authy_method, $availableMethods ) )
{
try
{
@@ -166,7 +166,7 @@ class _Handler extends \IPS\MFA\MFAHandler
}
catch ( Exception $e )
{
if ( in_array( $e->getCode(), array( Exception::TOKEN_REUSED, Exception::TOKEN_INVALID ) ) )
if ( \in_array( $e->getCode(), array( Exception::TOKEN_REUSED, Exception::TOKEN_INVALID ) ) )
{
$_SESSION['authyAuthError'] = $e->getUserMessage();
}
@@ -196,7 +196,7 @@ class _Handler extends \IPS\MFA\MFAHandler
/* Call Authy */
$availableMethods = explode( ',', \IPS\Settings::i()->authy_setup );
$method = ( isset( \IPS\Request::i()->method ) and in_array( \IPS\Request::i()->method, $availableMethods ) ) ? \IPS\Request::i()->method : array_shift( $availableMethods );
$method = ( isset( \IPS\Request::i()->method ) and \in_array( \IPS\Request::i()->method, $availableMethods ) ) ? \IPS\Request::i()->method : array_shift( $availableMethods );
$_SESSION['authyConfigureError'] = NULL;
try
{
@@ -230,7 +230,7 @@ class _Handler extends \IPS\MFA\MFAHandler
}
catch ( Exception $e )
{
if ( in_array( $e->getCode(), array( Exception::USER_INVALID, Exception::PHONE_NUMBER_INVALID ) ) )
if ( \in_array( $e->getCode(), array( Exception::USER_INVALID, Exception::PHONE_NUMBER_INVALID ) ) )
{
$_SESSION['authyConfigureError'] = $e->getUserMessage();
}
@@ -297,19 +297,19 @@ class _Handler extends \IPS\MFA\MFAHandler
}
/* If Authy app is one of the available options... */
if ( in_array( 'authy', $availableMethods ) )
if ( \in_array( 'authy', $availableMethods ) )
{
/* Are we getting a onetouch status? */
if ( \IPS\Request::i()->onetouchCheck and \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->json( array( 'status' => intval( $this->_onetouchCheck( $member, \IPS\Request::i()->onetouchCheck ) ) ) );
\IPS\Output::i()->json( array( 'status' => \intval( $this->_onetouchCheck( $member, \IPS\Request::i()->onetouchCheck ) ) ) );
}
/* If it is not the only option... */
if ( count( $availableMethods ) > 1 )
if ( \count( $availableMethods ) > 1 )
{
/* If they have asked for a text/call instead, do that */
if ( !isset( $mfaDetails['authy']['sent'] ) and isset( \IPS\Request::i()->authy_method ) and in_array( \IPS\Request::i()->authy_method, $availableMethods ) )
if ( !isset( $mfaDetails['authy']['sent'] ) and isset( \IPS\Request::i()->authy_method ) and \in_array( \IPS\Request::i()->authy_method, $availableMethods ) )
{
try
{
@@ -368,7 +368,7 @@ class _Handler extends \IPS\MFA\MFAHandler
}
/* If text message is the only available option, or we have chosen that option, do that... */
if ( in_array( 'sms', $availableMethods ) and ( count( $availableMethods ) == 1 ) or ( isset( $mfaDetails['authy']['sent'] ) and $mfaDetails['authy']['sent']['method'] == 'sms' ) or ( isset( \IPS\Request::i()->authy_method ) and \IPS\Request::i()->authy_method == 'sms' ) )
if ( \in_array( 'sms', $availableMethods ) and ( \count( $availableMethods ) == 1 ) or ( isset( $mfaDetails['authy']['sent'] ) and $mfaDetails['authy']['sent']['method'] == 'sms' ) or ( isset( \IPS\Request::i()->authy_method ) and \IPS\Request::i()->authy_method == 'sms' ) )
{
/* Send the text if we haven't already */
if ( !isset( $mfaDetails['authy']['sent'] ) )
@@ -418,7 +418,7 @@ class _Handler extends \IPS\MFA\MFAHandler
}
/* Otherwise we're going to show a screen */
return \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->authyAuthenticate( count( $availableMethods ) == 1 ? 'phone' : 'choose', FALSE, isset( $_SESSION['authyAuthError'] ) ? $_SESSION['authyAuthError'] : 'authy_error', FALSE, $availableMethods, NULL, $url );
return \IPS\Theme::i()->getTemplate( 'login', 'core', 'global' )->authyAuthenticate( \count( $availableMethods ) == 1 ? 'phone' : 'choose', FALSE, isset( $_SESSION['authyAuthError'] ) ? $_SESSION['authyAuthError'] : 'authy_error', FALSE, $availableMethods, NULL, $url );
}
/**
@@ -522,7 +522,7 @@ class _Handler extends \IPS\MFA\MFAHandler
}
catch ( Exception $e )
{
if ( in_array( $e->getCode(), array( Exception::TOKEN_REUSED, Exception::TOKEN_INVALID ) ) )
if ( \in_array( $e->getCode(), array( Exception::TOKEN_REUSED, Exception::TOKEN_INVALID ) ) )
{
$_SESSION['authyAuthError'] = $e->getUserMessage();
}
@@ -680,11 +680,11 @@ class _Handler extends \IPS\MFA\MFAHandler
{
$availableMethods = explode( ',', \IPS\Settings::i()->authy_method );
if ( in_array( 'authy', $availableMethods ) )
if ( \in_array( 'authy', $availableMethods ) )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_authy_title');
}
elseif ( in_array( 'sms', $availableMethods ) and count( $availableMethods ) == 1 )
elseif ( \in_array( 'sms', $availableMethods ) and \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_sms_title');
}
@@ -703,9 +703,9 @@ class _Handler extends \IPS\MFA\MFAHandler
{
$availableMethods = explode( ',', \IPS\Settings::i()->authy_method );
if ( in_array( 'authy', $availableMethods ) )
if ( \in_array( 'authy', $availableMethods ) )
{
if ( count( $availableMethods ) == 1 )
if ( \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_authy_only_desc_user');
}
@@ -714,11 +714,11 @@ class _Handler extends \IPS\MFA\MFAHandler
return \IPS\Member::loggedIn()->language()->addToStack('mfa_authy_mixed_desc_user');
}
}
elseif ( in_array( 'sms', $availableMethods ) and count( $availableMethods ) == 1 )
elseif ( \in_array( 'sms', $availableMethods ) and \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_sms_desc_user');
}
elseif ( in_array( 'phone', $availableMethods ) and count( $availableMethods ) == 1 )
elseif ( \in_array( 'phone', $availableMethods ) and \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_phone_desc_user');
}
@@ -737,11 +737,11 @@ class _Handler extends \IPS\MFA\MFAHandler
{
$availableMethods = explode( ',', \IPS\Settings::i()->authy_method );
if ( in_array( 'authy', $availableMethods ) and count( $availableMethods ) == 1 )
if ( \in_array( 'authy', $availableMethods ) and \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_authy_recovery');
}
elseif ( in_array( 'sms', $availableMethods ) and count( $availableMethods ) == 1 )
elseif ( \in_array( 'sms', $availableMethods ) and \count( $availableMethods ) == 1 )
{
return \IPS\Member::loggedIn()->language()->addToStack('mfa_sms_recovery');
}
@@ -813,6 +813,7 @@ class _Handler extends \IPS\MFA\MFAHandler
/**
* Verify an Authy API Key
*
* @param string $val The API key submitted
* @return array
* @throws \DomainException
*/
View File
Whitespace-only changes.
+12 -12
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA\GoogleAuthenticator;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -77,15 +77,15 @@ class _Handler extends \IPS\MFA\MFAHandler
}
else
{
if ( function_exists( 'random_bytes' ) )
if ( \function_exists( 'random_bytes' ) )
{
$randomString = random_bytes( 16 );
}
elseif ( function_exists( 'mcrypt_create_iv' ) )
elseif ( \function_exists( 'mcrypt_create_iv' ) )
{
$randomString = mcrypt_create_iv( 16, MCRYPT_DEV_URANDOM );
}
elseif ( function_exists( 'openssl_random_pseudo_bytes' ) )
elseif ( \function_exists( 'openssl_random_pseudo_bytes' ) )
{
$randomString = openssl_random_pseudo_bytes( 16 );
}
@@ -97,7 +97,7 @@ class _Handler extends \IPS\MFA\MFAHandler
$secret = '';
for ( $i = 0; $i < 16; ++$i )
{
$secret .= $validChars[ ord( $randomString[ $i ] ) & 31 ];
$secret .= $validChars[ \ord( $randomString[ $i ] ) & 31 ];
}
}
@@ -151,7 +151,7 @@ class _Handler extends \IPS\MFA\MFAHandler
{
try
{
$waitUntil = ( \IPS\Db::i()->select( 'time', 'core_googleauth_used_codes', array( 'member=?', $member->member_id ), 'time DESC', 1 )->first() * 30 ) + 30;
$waitUntil = ( \IPS\Db::i()->select( 'time', 'core_googleauth_used_codes', array( '`member`=?', $member->member_id ), 'time DESC', 1 )->first() * 30 ) + 30;
}
catch ( \UnderflowException $e )
{
@@ -257,13 +257,13 @@ class _Handler extends \IPS\MFA\MFAHandler
$submittedCode = str_replace( ' ', '', $submittedCode );
$validTimes = array( new \IPS\DateTime(), ( new \IPS\DateTime() )->add( new \DateInterval('PT30S') ), ( new \IPS\DateTime() )->sub( new \DateInterval('PT30S') ) );
$blockedTimes = iterator_to_array( \IPS\Db::i()->select( 'time', 'core_googleauth_used_codes', array( 'member=?', $member->member_id ) ) );
$blockedTimes = iterator_to_array( \IPS\Db::i()->select( 'time', 'core_googleauth_used_codes', array( '`member`=?', $member->member_id ) ) );
$allowedCodes = array();
foreach ( $validTimes as $time )
{
$codeTime = floor( $time->getTimestamp() / 30 );
if ( !in_array( $codeTime, $blockedTimes ) )
if ( !\in_array( $codeTime, $blockedTimes ) )
{
$allowedCodes[ static::getCodeForSecretAtTime( $secret, $time ) ] = $codeTime;
}
@@ -301,17 +301,17 @@ class _Handler extends \IPS\MFA\MFAHandler
$block .= str_pad( base_convert( $chars[ $secret[ $i + $j ] ], 10, 2 ), 5, '0', STR_PAD_LEFT );
}
$eightBits = str_split( $block, 8 );
for ( $z = 0; $z < count( $eightBits ); ++$z )
for ( $z = 0; $z < \count( $eightBits ); ++$z )
{
$decodedSecretKey .= ( ( $y = chr( base_convert( $eightBits[ $z ], 2, 10) ) ) || ord( $y ) == 48) ? $y : '';
$decodedSecretKey .= ( ( $y = \chr( base_convert( $eightBits[ $z ], 2, 10) ) ) || \ord( $y ) == 48) ? $y : '';
}
}
/* Hash the timestamp with the secret key */
$hash = hash_hmac('SHA1', chr(0).chr(0).chr(0).chr(0).pack('N*', floor( $time->getTimestamp() / 30 ) ), $decodedSecretKey, true);
$hash = hash_hmac('SHA1', \chr(0).\chr(0).\chr(0).\chr(0).pack('N*', floor( $time->getTimestamp() / 30 ) ), $decodedSecretKey, true);
/* Unpack it */
$value = unpack( 'N', \substr( $hash, ord( \substr( $hash, -1 ) ) & 0x0F, 4 ) );
$value = unpack( 'N', \substr( $hash, \ord( \substr( $hash, -1 ) ) & 0x0F, 4 ) );
$value = $value[1];
/* Get 32 bits */
Whitespace-only changes.
+18 -6
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -52,7 +52,20 @@ abstract class _MFAHandler
'questions' => new \IPS\MFA\SecurityQuestions\Handler()
);
}
/**
* Removes any previous authentication settings for this user
*
* @return void
*/
public static function resetAuthentication()
{
if ( isset( $_SESSION['MFAAuthenticated'] ) )
{
unset( $_SESSION['MFAAuthenticated'] );
}
}
/**
* Display output when trying to access an area
*
@@ -73,7 +86,7 @@ abstract class _MFAHandler
}
/* If MFA is not enabled for this area, do nothing */
if ( !\IPS\Settings::i()->security_questions_areas or !in_array( "{$app}_{$area}", explode( ',', \IPS\Settings::i()->security_questions_areas ) ) )
if ( !\IPS\Settings::i()->security_questions_areas or !\in_array( "{$app}_{$area}", explode( ',', \IPS\Settings::i()->security_questions_areas ) ) )
{
return NULL;
}
@@ -150,7 +163,7 @@ abstract class _MFAHandler
/* Display */
$knownDevicesAvailable = FALSE;
if ( $app === 'core' and $area === 'AuthenticateFront' and !in_array( "app_AuthenticateFrontKnown", explode( ',', \IPS\Settings::i()->security_questions_areas ) ) )
if ( $app === 'core' and $area === 'AuthenticateFront' and !\in_array( "app_AuthenticateFrontKnown", explode( ',', \IPS\Settings::i()->security_questions_areas ) ) )
{
if ( \IPS\Db::i()->select( 'COUNT(*)', 'core_members_known_devices', array( 'member_id=?', $member->member_id ) )->first() )
{
@@ -188,7 +201,7 @@ abstract class _MFAHandler
\IPS\Session::i()->csrfCheck();
foreach ( $acceptableHandlers as $key => $handler )
{
if ( ( count( $acceptableHandlers ) == 1 ) or $key == \IPS\Request::i()->mfa_method )
if ( ( \count( $acceptableHandlers ) == 1 ) or $key == \IPS\Request::i()->mfa_method )
{
if ( $handler->configurationScreenSubmit( $member ) )
{
@@ -371,7 +384,6 @@ abstract class _MFAHandler
* Configuration options when editing member account in ACP
*
* @param \IPS\Member $member The member
* @param \IPS\Helpers\Form $form Set to TRUE if multiple options are being displayed
* @return array
*/
public function acpConfiguration( \IPS\Member $member )
+6 -5
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA\SecurityQuestions;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
@@ -42,6 +42,7 @@ class _Handler extends \IPS\MFA\MFAHandler
/**
* Member *can* use this handler (even if they have not yet configured it)
*
* @param \IPS\Member $member Member to check
* @return bool
*/
public function memberCanUseHandler( \IPS\Member $member )
@@ -52,7 +53,7 @@ class _Handler extends \IPS\MFA\MFAHandler
/**
* Member has configured this handler
*
* @param \IPS\Member $member The member
* @param \IPS\Member $member Member to check
* @return bool
*/
public function memberHasConfiguredHandler( \IPS\Member $member )
@@ -100,7 +101,7 @@ class _Handler extends \IPS\MFA\MFAHandler
);
}
if ( count( $answers ) >= \IPS\Settings::i()->security_questions_number )
if ( \count( $answers ) >= \IPS\Settings::i()->security_questions_number )
{
\IPS\Db::i()->delete( 'core_security_answers', array( 'answer_member_id=?', $member->member_id ) );
\IPS\Db::i()->insert( 'core_security_answers', $answers );
@@ -329,12 +330,12 @@ class _Handler extends \IPS\MFA\MFAHandler
}
}
if ( count( $toInsert ) )
if ( \count( $toInsert ) )
{
\IPS\Db::i()->insert( 'core_security_answers', $toInsert );
}
if ( count( $toInsert ) >= \IPS\Settings::i()->security_questions_number )
if ( \count( $toInsert ) >= \IPS\Settings::i()->security_questions_number )
{
$member->members_bitoptions['has_security_answers'] = TRUE;
}
+1 -1
View File
@@ -11,7 +11,7 @@
namespace IPS\MFA\SecurityQuestions;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
Whitespace-only changes.
View File
Whitespace-only changes.