Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+78 -562
View File
@@ -1,11 +1,12 @@
<?php
/**
* @brief Abstract Login Handler
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
* @copyright (c) Invision Power Services, Inc.
* @license https://www.invisioncommunity.com/legal/standards/
* @package Invision Community
* @since 15 Mar 2013
* @brief 4.2 and below Backwards Compatibilty Login Handler
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Community Suite
* @since 16 May 2017
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Login;
@@ -18,279 +19,97 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
}
/**
* Abstract Login Handler
* 4.2 and below Backwards Compatibilty Login Handler
*
* @deprecated This class exists only to provide backwards compatibility for login handlers created before 4.3
*/
abstract class _LoginAbstract extends \IPS\Node\Model
{
abstract class _LoginAbstract extends \IPS\Login\Handler
{
/**
* @brief [ActiveRecord] Multiton Store
*/
protected static $multitons;
/**
* @brief [ActiveRecord] Database Table
*/
public static $databaseTable = 'core_login_handlers';
/**
* @brief [ActiveRecord] Database Prefix
*/
public static $databasePrefix = 'login_';
/**
* @brief [ActiveRecord] ID Database Column
*/
public static $databaseColumnId = 'key';
/**
* @brief [Node] Node Title
*/
public static $nodeTitle = 'login_handlers';
/**
* @brief [Node] Order Database Column
*/
public static $databaseColumnOrder = 'order';
/**
* @brief Icon
*/
public static $icon = 'lock';
/**
* @brief Logo
*/
public static $logo = NULL;
/**
* @brief Disable the copy button - useful when the forms are very distinctly different
*/
public $noCopyButton = TRUE;
/**
* Construct ActiveRecord from database row
*
* @param array $data Row from database table
* @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there?
* @return static
*/
public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE )
{
/* Initiate an object */
$classname = '\\IPS\Login\\' . ucfirst( $data['login_key'] );
if ( !class_exists( $classname ) )
{
throw new \RuntimeException;
}
$obj = new $classname;
$obj->_new = FALSE;
/* Import data */
$databasePrefixLength = \strlen( static::$databasePrefix );
foreach ( $data as $k => $v )
{
if( static::$databasePrefix )
{
$k = \substr( $k, $databasePrefixLength );
}
$obj->$k = $v;
}
$obj->changed = array();
$obj->init();
/* Return */
return $obj;
}
/**
* [Node] Get Node Title
* Get title
*
* @return string
*/
protected function get__title()
public static function getTitle()
{
$key = "login_handler_" . ucfirst($this->key);
return \IPS\Member::loggedIn()->language()->addToStack( $key );
}
/**
* [Node] Get the title to store in the log
*
* @return string|null
*/
public function titleForLog()
{
return $this->_logKey;
}
/**
* [Node] Get the log key
*
* @return string|null
*/
protected function get__logKey()
{
return \IPS\Lang::load( \IPS\Lang::defaultLanguage() )->get( 'login_handler_' . ucfirst( $this->key ) );
}
/**
* [Node] Get whether or not this node is enabled
*
* @note Return value NULL indicates the node cannot be enabled/disabled
* @return bool|null
*/
protected function get__enabled()
{
return $this->enabled;
return 'login_handler_' . mb_substr( get_called_class(), 10 );
}
/**
* [Node] Set whether or not this node is enabled
* Get type
*
* @param bool $value Enable Node?
* @return void
* @return int
*/
protected function set__enabled( $value )
public function type()
{
$this->enabled = $value;
}
/**
* [Node] Does the currently logged in user have permission to add a child node to this node?
*
* @return bool
*/
public function canAdd()
{
return FALSE;
}
/**
* [Node] Does the currently logged in user have permission to add aa root node?
*
* @return bool
*/
public static function canAddRoot()
{
return FALSE;
}
/**
* [Node] Add/Edit Form
*
* @param \IPS\Helpers\Form $form The form
* @return void
*/
public function form( &$form )
{
foreach ( $this->acpForm() as $k => $v )
if ( method_exists( $this, 'loginForm' ) )
{
if ( is_string( $v ) )
{
$form->addHeader( $v );
}
else
{
$form->add( $v );
$names[ $v->name ] = $k;
}
return \IPS\Login::TYPE_BUTTON;
}
if ( mb_strtolower( $this->key ) !== 'internal' )
else
{
$form->add( new \IPS\Helpers\Form\YesNo( 'login_acp', $this->acp ) );
return \IPS\Login::TYPE_USERNAME_PASSWORD;
}
if ( isset( static::$shareService ) )
}
/**
* Get auth type
*
* @return int
*/
public function authType()
{
return $this->authTypes;
}
/**
* Get button
*
* @return string
*/
public function button()
{
$destination = NULL;
if ( isset( \IPS\Request::i()->ref ) )
{
try
{
$shareService = \IPS\core\ShareLinks\Service::load( static::$shareService, 'share_key' );
$form->add( new \IPS\Helpers\Form\YesNo( 'share_autoshare_' . $this->key, $shareService->autoshare ) );
$url = \IPS\Http\Url::createFromString( base64_decode( \IPS\Request::i()->ref ) );
if ( $url instanceof \IPS\Http\Url\Internal )
{
$destination = $url;
}
}
catch ( \OutOfRangeException $e ) { }
catch ( \Exception $e ) { }
}
return $this->loginForm( \IPS\Request::i()->url(), FALSE, $destination );
}
/**
* [Node] Format form values from add/edit form for save
* Authenticate
*
* @param array $values Values from the form
* @return array
* @param \IPS\Login $login The login object
* @param string $usernameOrEmail The username or email address provided by the user
* @param string $password The plaintext password provided by the user
* @return \IPS\Member
* @throws \IPS\Login\Exception
*/
public function formatFormValues( $values )
public function authenticateUsernamePassword( \IPS\Login $login, $usernameOrEmail, $password )
{
$names = array();
foreach ( $this->acpForm() as $k => $v )
{
if ( !is_string( $v ) )
{
$names[ $v->name ] = $k;
}
}
$save = array();
foreach ( $names as $formName => $saveName )
{
if( array_key_exists( $formName, $values ) )
{
$save[ $saveName ] = $values[ $formName ];
unset( $values[ $formName ] );
}
}
if ( isset( $values[ 'share_autoshare_' . $this->key ] ) )
{
$save['autoshare'] = $values[ 'share_autoshare_' . $this->key ];
}
$values['settings'] = json_encode( $save );
$this->settings = $values['settings'];
$this->testSettings();
if ( mb_strtolower( $this->key ) == 'internal' )
{
$values['login_acp'] = 1;
}
return $values;
return $this->authenticate( array( 'auth' => $usernameOrEmail, 'password' => $password ) );
}
/**
* [Node] Save Add/Edit Form
* Authenticate
*
* @param array $values Values from the form
* @return void
* @param \IPS\Login $login The login object
* @return \IPS\Member
* @throws \IPS\Login\Exception
*/
public function saveForm( $values )
public function authenticateButton( \IPS\Login $login )
{
if ( isset( $values[ 'share_autoshare_' . $this->key ] ) )
{
try
{
$shareService = \IPS\core\ShareLinks\Service::load( static::$shareService, 'share_key' );
$shareService->autoshare = $values[ 'share_autoshare_' . $this->key ];
$shareService->save();
}
catch ( \OutOfRangeException $e ) { }
unset( $values[ 'share_autoshare_' . $this->key ] );
}
parent::saveForm( $values );
}
/**
* [ActiveRecord] Save Changed Columns
*
* @return void
*/
function save()
{
parent::save();
unset( \IPS\Data\Store::i()->loginHandlers );
return $this->authenticate( $login->url );
}
/**
@@ -311,67 +130,17 @@ abstract class _LoginAbstract extends \IPS\Node\Model
/* Create an account */
if ( !$member or !$member->member_id )
{
/* Is registration enabled? */
if( !\IPS\Settings::i()->allow_reg )
try
{
$exception = new \IPS\Login\Exception( 'reg_disabled', \IPS\Login\Exception::REGISTRATION_DISABLED );
$exception->handler = mb_substr( get_called_class(), 10 );
$exception->member = $member;
throw $exception;
$member = $this->createAccount( $name, $email );
}
/* Init */
$member = new \IPS\Member;
$member->member_group_id = \IPS\Settings::i()->member_group;
$member->members_bitoptions['view_sigs'] = TRUE;
/* Set name - if it already exists, we'll leave it blank and they'll be prompted to fill it in */
if ( $name !== NULL and ( !\IPS\Settings::i()->username_characters or preg_match( '/^[' . str_replace( '\-', '-', preg_quote( \IPS\Settings::i()->username_characters, '/' ) ) . ']*$/iu', $name ) ) )
catch ( \IPS\Login\Exception $e )
{
$existingUsername = \IPS\Member::load( $name, 'name' );
if ( !$existingUsername->member_id )
if ( $details )
{
$member->name = $name;
$e->details = $details;
}
}
/* Set email */
if ( $email !== NULL )
{
/* Check it doesn't already exist */
$existingEmail = \IPS\Member::load( $email, 'email' );
if ( $existingEmail->member_id )
{
$exception = new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::MERGE_SOCIAL_ACCOUNT );
$exception->handler = mb_substr( get_called_class(), 10 );
$exception->member = $existingEmail;
if ( $details )
{
$exception->details = $details;
}
throw $exception;
}
/* Set it */
$member->email = $email;
/* Check the spam service is okay with it */
if( \IPS\Settings::i()->spam_service_enabled )
{
if( $member->spamService() == 4 )
{
$exception = new \IPS\Login\Exception( 'spam_denied_account', \IPS\Login\Exception::REGISTRATION_DENIED_BY_SPAM_SERVICE );
$exception->handler = mb_substr( get_called_class(), 10 );
$exception->member = $member;
throw $exception;
}
}
}
/* Set profile sync */
if ( $profileSync !== NULL )
{
$member->profilesync = json_encode( array( mb_substr( get_called_class(), 10 ) => $profileSync ) );
throw $e;
}
/* Member properties */
@@ -380,20 +149,6 @@ abstract class _LoginAbstract extends \IPS\Node\Model
$member->$k = $v;
}
$member->save();
/* Sync photo, etc now */
if ( $profileSync !== NULL )
{
$profileSyncClass = $profileSyncClass ?: 'IPS\core\ProfileSync\\' . mb_substr( get_called_class(), 10 );
$sync = new $profileSyncClass( $member );
$sync->sync();
}
/* If registration is complete, do post-registration stuff */
if ( $member->name and $member->email and !$member->members_bitoptions['bw_is_spammer'] )
{
$member->postRegistration( TRUE );
}
}
/* Or just update? */
@@ -414,265 +169,26 @@ abstract class _LoginAbstract extends \IPS\Node\Model
/* Return */
return $member;
}
/**
* Get settings
*
* @return array
*/
public function get_settings()
{
return json_decode( $this->_data['settings'], TRUE );
}
/**
* Initiate
*
* @return void
*/
public function init()
{
}
/**
* Test Settings
*
* @return bool
* @throws \LogicException
*/
public function testSettings()
{
return TRUE;
}
/**
* Perform an action when the login handler is disabled
* Can this handler process a password change for a member?
*
* @return bool
*/
public function onDisable()
public function canChangePassword( \IPS\Member $member )
{
return TRUE;
}
/**
* Fetch the type as a textual string
*
* @param int $type The valid configured types
* @return string
*/
public function getLoginType( $type )
{
switch ( $type )
{
case \IPS\Login::AUTH_TYPE_USERNAME + \IPS\Login::AUTH_TYPE_EMAIL:
return 'username_or_email';
break;
case \IPS\Login::AUTH_TYPE_USERNAME:
return 'username';
break;
case \IPS\Login::AUTH_TYPE_EMAIL:
return 'email_address';
break;
}
return 'username';
return method_exists( $this, 'canChange' ) ? $this->canChange( 'password', $member ) : FALSE;
}
/**
* Email is in use?
* Used when registering or changing an email address to check the new one is available
*
* @param string $email Email Address
* @param \IPS\Member|NULL $eclude Member to exclude
* @return bool|NULL Boolean indicates if email is in use (TRUE means is in use and thus not registerable) or NULL if this handler does not support such an API
*/
public function emailIsInUse( $email, \IPS\Member $exclude=NULL )
{
return NULL;
}
/**
* Username is in use?
* Used when registering or changing an username to check the new one is available
*
* @param string $username Username
* @return bool|NULL Boolean indicates if username is in use (TRUE means is in use and thus not registerable) or NULL if this handler does not support such an API
*/
public function usernameIsInUse( $username )
{
return NULL;
}
/**
* Change Email Address
* Link Account
*
* @param \IPS\Member $member The member
* @param string $oldEmail Old Email Address
* @param string $newEmail New Email Address
* @param mixed $details Details as they were passed to the exception
* @return void
* @throws \Exception
*/
public function changeEmail( \IPS\Member $member, $oldEmail, $newEmail )
public function completeLink( \IPS\Member $member, $details )
{
if ( !$this->canChange( 'email', $member ) )
{
throw new \BadMethodCallException;
}
}
/**
* Change Password
*
* @param \IPS\Member $member The member
* @param string $newPassword New Password
* @return void
* @throws \Exception
*/
public function changePassword( \IPS\Member $member, $newPassword )
{
if ( !$this->canChange( 'password', $member ) )
{
throw new \BadMethodCallException;
}
}
/**
* Change Username
*
* @param \IPS\Member $member The member
* @param string $oldUsername Old Username
* @param string $newUsername New Username
* @return void
* @throws \Exception
*/
public function changeUsername( \IPS\Member $member, $oldUsername, $newUsername )
{
// By default do nothing. Handlers can extend.
}
/**
* Log an account off
*
* @param \IPS\Member $member The member that was just logged out
* @param \IPS\Http\Url $redirectUrl The URL to send the user back to
* @return void
* @throws \Exception
* @note This is NOT called if you force log out all users from the ACP on an individual site
*/
public function logoutAccount( \IPS\Member $member, \IPS\Http\Url $redirectUrl )
{
// By default do nothing. Handlers can extend.
}
/**
* Create an account
*
* @param \IPS\Member $member The member that was just created
* @return void
* @throws \Exception
*/
public function createAccount( \IPS\Member $member )
{
// By default do nothing. Handlers can extend.
}
/**
* Validate account
*
* @param \IPS\Member $member The member
* @return void
* @throws \Exception
*/
public function validateAccount( \IPS\Member $member )
{
// By default do nothing. Handlers can extend.
}
/**
* Delete account
*
* @param \IPS\Member $member The member
* @return void
* @throws \Exception
*/
public function deleteAccount( \IPS\Member $member )
{
// By default do nothing. Handlers can extend.
}
/**
* Ban or unban account
*
* @param \IPS\Member $member The member
* @param bool TRUE means member is being banned, FALSE means they are being unbanned
* @return void
* @throws \Exception
*/
public function banAccount( \IPS\Member $member, $ban=TRUE )
{
// By default do nothing. Handlers can extend.
}
/**
* Merge two accounts
*
* @param \IPS\Member $member The member to keep with original data
* @param \IPS\Member $member2 The member that will be deleted
* @return void
* @throws \Exception
*/
public function mergeAccounts( \IPS\Member $member, \IPS\Member $member2 )
{
// By default do nothing. Handlers can extend.
}
/**
* Search
*
* @param string $column Column to search
* @param string $query Search query
* @param string|null $order Column to order by
* @param mixed $where Where clause
* @return array
*/
public static function search( $column, $query, $order, $where=array() )
{
if ( $column === '_title' )
{
$return = array();
foreach ( \IPS\Member::loggedIn()->language()->searchCustom( 'login_handler_', $query, TRUE ) as $key => $value )
{
try
{
$return[ $key ] = static::load( $key );
}
catch ( \OutOfRangeException $e ) { }
}
return $return;
}
}
/**
* Can a member sign in with this login handler?
* Used to ensure when a user disassociates a social login that they have some other way of logging in
*
* @param \IPS\Member $member The member
* @return bool
*/
abstract function canProcess( \IPS\Member $member );
/**
* [Node] Does the currently logged in user have permission to delete this node?
*
* @return bool
*/
public function canDelete()
{
return FALSE;
static::link( $member, $details );
}
}