Version 4.3.6

This commit is contained in:
Neo committed 2025-12-19 05:49:12 -08:00
1 parent fe1acf984a
commit 1a0c7fd3c2
609 files changed
+14707 -5726

No files matched your search

@@ -56,7 +56,7 @@ class _ExternalDatabase extends \IPS\Login\Handler
'login_external_conn',
'sql_host' => new \IPS\Helpers\Form\Text( 'login_external_host', ( isset( $this->settings['sql_host'] ) and $this->settings['sql_host'] ) ? $this->settings['sql_host'] : 'localhost', TRUE ),
'sql_user' => new \IPS\Helpers\Form\Text( 'login_external_user', isset( $this->settings['sql_user'] ) ? $this->settings['sql_user'] : NULL, TRUE ),
'sql_pass' => new \IPS\Helpers\Form\Text( 'login_external_pass', isset( $this->settings['sql_pass'] ) ? $this->settings['sql_pass'] : NULL, TRUE ),
'sql_pass' => new \IPS\Helpers\Form\Text( 'login_external_pass', isset( $this->settings['sql_pass'] ) ? $this->settings['sql_pass'] : NULL, FALSE ),
'sql_database' => new \IPS\Helpers\Form\Text( 'login_external_database', isset( $this->settings['sql_database'] ) ? $this->settings['sql_database'] : NULL, TRUE ),
'sql_port' => new \IPS\Helpers\Form\Number( 'login_external_port', isset( $this->settings['sql_port'] ) ? $this->settings['sql_port'] : 3306, FALSE ),
'sql_socket' => new \IPS\Helpers\Form\Text( 'login_external_socket', isset( $this->settings['sql_socket'] ) ? $this->settings['sql_socket'] : NULL, FALSE ),
@@ -222,6 +222,8 @@ class _ExternalDatabase extends \IPS\Login\Handler
$settings['update_name_changes'] = $_values['login_update_name_changes_inc_optional'];
$settings['update_email_changes'] = $_values['login_update_email_changes_inc_optional'];
}
$settings['forgot_password_url'] = (string) $settings['forgot_password_url'];
unset( $settings['update_name_changes_inc_optional'] );
unset( $settings['update_name_changes_no_optional'] );
@@ -935,7 +937,7 @@ class _ExternalDatabase extends \IPS\Login\Handler
return sha1( $providedPassword );
case 'password_hash':
return password_hash( $providedPassword );
return password_hash( $providedPassword, PASSWORD_DEFAULT );
case 'other':
try
+14 -2
View File
@@ -150,7 +150,7 @@ abstract class _Handler extends \IPS\Node\Model
{
try
{
$this->_cachedLinks[ $member->member_id ] = \IPS\Db::i()->select( '*', 'core_login_links', array( 'token_login_method=? AND token_member=? AND token_linked=1', $this->id, $member->member_id ) )->first();
$this->_cachedLinks[ $member->member_id ] = \IPS\Db::i()->select( '*', 'core_login_links', array( 'token_login_method=? AND token_member=? AND token_linked=1', $this->id, $member->member_id ), NULL, NULL, NULL, NULL, \IPS\Db::SELECT_FROM_WRITE_SERVER )->first();
}
catch ( \UnderflowException $e )
{
@@ -340,6 +340,7 @@ abstract class _Handler extends \IPS\Node\Model
public function completeLink( \IPS\Member $member, $details )
{
\IPS\Db::i()->update( 'core_login_links', array( 'token_linked' => 1 ), array( 'token_login_method=? AND token_member=?', $this->id, $member->member_id ) );
unset( $this->_cachedLinks[ $member->member_id ] );
$member->logHistory( 'core', 'social_account', array(
'service' => static::getTitle(),
@@ -809,6 +810,17 @@ abstract class _Handler extends \IPS\Node\Model
return parent::formatFormValues( $values );
}
/**
* Test Compatibility
*
* @return bool
* @throws \LogicException
*/
public static function testCompatibility()
{
return TRUE;
}
/**
* Test Settings
@@ -818,7 +830,7 @@ abstract class _Handler extends \IPS\Node\Model
*/
public function testSettings()
{
return TRUE;
return static::testCompatibility();
}
/**
+12 -6
View File
@@ -37,7 +37,7 @@ abstract class _OAuth1 extends \IPS\Login\Handler
public function acpForm()
{
$return = array(
array( 'login_handler_oauth_settings', static::getTitle() . '_info' ),
array( 'login_handler_oauth_settings', \IPS\Member::loggedIn()->language()->addToStack( static::getTitle() . '_info', FALSE, array( 'sprintf' => array( (string) $this->redirectionEndpoint() ) ) ) ),
'consumer_key' => new \IPS\Helpers\Form\Text( 'oauth_consumer_key', ( isset( $this->settings['consumer_key'] ) ) ? $this->settings['consumer_key'] : '', TRUE ),
'consumer_secret' => new \IPS\Helpers\Form\Text( 'oauth_consumer_secret', ( isset( $this->settings['consumer_secret'] ) ) ? $this->settings['consumer_secret'] : '', TRUE ),
);
@@ -125,11 +125,7 @@ abstract class _OAuth1 extends \IPS\Login\Handler
*/
protected function _redirectToAuthorizationEndpoint( \IPS\Login $login )
{
$callback = $login->url->setQueryString( '_processLogin', $this->id )->csrf();
if ( isset( \IPS\Request::i()->ref ) )
{
$callback = $callback->setQueryString( 'ref', \IPS\Request::i()->ref );
}
$callback = $this->redirectionEndpoint()->setQueryString( 'state' , $this->id . '-' . base64_encode( $login->url ) . '-' . \IPS\Session::i()->csrfKey . '-' . \IPS\Request::i()->ref );
try
{
@@ -297,6 +293,16 @@ abstract class _OAuth1 extends \IPS\Login\Handler
* @return \IPS\Http\Url
*/
abstract protected function accessTokenEndpoint();
/**
* Redirection Endpoint
*
* @return \IPS\Http\Url
*/
public function redirectionEndpoint()
{
return \IPS\Http\Url::internal( 'oauth/callback/', 'none' );
}
/**
* Send Request
+5
View File
@@ -454,6 +454,11 @@ class _Twitter extends \IPS\Login\Handler\OAuth1
{
$response = $this->_sendRequest( 'get', \IPS\Http\Url::external('https://api.twitter.com/1.1/account/verify_credentials.json'), array(), $accessToken, $accessTokenSecret );
if ( $response->httpResponseCode == 401 )
{
return FALSE;
}
if ( $response->httpHeaders['x-access-level'] == 'read' )
{
return FALSE;
+10 -3
View File
@@ -102,9 +102,9 @@ class _Custom extends \IPS\Login\Handler\OAuth2
}
}
};
$return['authorization_endpoint'] = new \IPS\Helpers\Form\Url( 'oauth_authorization_endpoint', isset( $this->settings['authorization_endpoint'] ) ? $this->settings['authorization_endpoint'] : NULL, NULL, array( 'placeholder' => 'https://example.com/oauth/authorize' ), $authorizationEndpointValidation, NULL, NULL, 'authorization_endpoint' );
$return['authorization_endpoint_secure'] = new \IPS\Helpers\Form\Url( 'oauth_authorization_endpoint_secure', isset( $this->settings['authorization_endpoint_secure'] ) ? $this->settings['authorization_endpoint_secure'] : NULL, NULL, array( 'nullLang' => 'oauth_authorization_endpoint_same', 'placeholder' => 'https://example.com/oauth/authorize/?prompt=login' ), $authorizationEndpointValidation, NULL, NULL, 'authorization_endpoint_secure' );
$return['authorization_endpoint_secure'] = new \IPS\Helpers\Form\Url( 'oauth_authorization_endpoint_secure', ( isset( $this->settings['authorization_endpoint_secure'] ) AND $this->settings['authorization_endpoint_secure'] ) ? $this->settings['authorization_endpoint_secure'] : NULL, NULL, array( 'nullLang' => 'oauth_authorization_endpoint_same', 'placeholder' => 'https://example.com/oauth/authorize/?prompt=login' ), $authorizationEndpointValidation, NULL, NULL, 'authorization_endpoint_secure' );
$return['token_endpoint'] = new \IPS\Helpers\Form\Url( 'oauth_token_endpoint', isset( $this->settings['token_endpoint'] ) ? $this->settings['token_endpoint'] : NULL, TRUE, array( 'placeholder' => 'https://www.example.com/oauth/token' ) );
$return['user_endpoint'] = new \IPS\Helpers\Form\Url( 'oauth_user_endpoint', isset( $this->settings['user_endpoint'] ) ? $this->settings['user_endpoint'] : NULL, TRUE, array( 'placeholder' => 'https://www.example.com/oauth/me' ) );
$return['uid_field'] = new \IPS\Helpers\Form\Text( 'oauth_custom_uid_field', isset( $this->settings['uid_field'] ) ? $this->settings['uid_field'] : NULL, TRUE, array() );
@@ -442,7 +442,14 @@ class _Custom extends \IPS\Login\Handler\OAuth2
->decodeJson();
}
}
/* Check for any errors */
if ( static::getValueFromArray( $response, $this->settings['uid_field'] ) === NULL )
{
\IPS\Log::log( print_r( $response, TRUE ), 'oauth_custom' );
throw new \IPS\Login\Exception( 'generic_error', \IPS\Login\Exception::INTERNAL_ERROR );
}
/* Set */
$this->_cachedUserData[ $accessToken ] = $response;
}
+80 -50
View File
@@ -22,11 +22,6 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Facebook extends \IPS\Login\Handler\OAuth2
{
/**
* @brief Share Service
*/
public static $shareService = 'Facebook';
/**
* Get title
*
@@ -71,6 +66,22 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
return $return;
}
/**
* Test Compatibility
*
* @return bool
* @throws \LogicException
*/
public static function testCompatibility()
{
if ( mb_substr( \IPS\Settings::i()->base_url, 0, 8 ) !== 'https://' )
{
throw new \LogicException( \IPS\Member::loggedIn()->language()->addToStack( \IPS\CIC ? 'login_facebook_https_cic' : 'login_facebook_https' ) );
}
return TRUE;
}
/**
* Get the button color
@@ -143,25 +154,12 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
{
$return = array('email');
if ( isset( static::$shareService ) )
{
try
{
$shareService = \IPS\core\ShareLinks\Service::load( static::$shareService, 'share_key' );
if ( $shareService->autoshare )
{
$return[] = 'publish_actions';
}
}
catch ( \OutOfRangeException $e ) { }
}
if ( \IPS\Settings::i()->profile_comments and isset( $this->settings['allow_status_import'] ) and $this->settings['allow_status_import'] )
{
$return[] = 'user_posts';
}
$additionalPermitted = array( 'manage_pages', 'publish_pages', 'user_managed_groups', 'publish_actions' );
$additionalPermitted = array( 'manage_pages', 'publish_pages', 'publish_to_groups' );
if ( $additional !== NULL )
{
foreach( $additional as $scope )
@@ -186,7 +184,9 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
{
try
{
$response = $this->_authorizedRequest( 'me/permissions', $accessToken );
$response = $this->_authorizedRequest( 'me/permissions', $accessToken, array(
'appsecret_proof' => hash_hmac( 'sha256', $accessToken, $this->settings['client_secret'] )
), 'get' );
}
catch ( \Exception $e )
{
@@ -406,22 +406,6 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
return $return;
}
/**
* Get link to user's remote profile
* May return NULL if server doesn't support this
*
* @param string $identifier The ID Nnumber/string from remote service
* @param string $username The username from remote service
* @return \IPS\Http\Url|NULL
* @throws \IPS\Login\Exception The token is invalid and the user needs to reauthenticate
* @throws \DomainException General error where it is safe to show a message to the user
* @throws \RuntimeException Unexpected error from service
*/
public function userLink( $identifier, $username )
{
return \IPS\Http\Url::external( "https://www.facebook.com/" . $identifier );
}
/**
* Syncing Options
*
@@ -432,8 +416,9 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
public function syncOptions( \IPS\Member $member, $defaultOnly = FALSE )
{
$return = array();
if ( ( !isset( $this->settings['update_email_changes'] ) or $this->settings['update_email_changes'] === 'optional' ) and in_array( 'email', $this->authorizedScopes( $member ) ) )
$scopes = $this->authorizedScopes( $member );
if ( ( !isset( $this->settings['update_email_changes'] ) or $this->settings['update_email_changes'] === 'optional' ) and ( $scopes and in_array( 'email', $scopes ) ) )
{
$return[] = 'email';
}
@@ -446,7 +431,7 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
$return[] = 'photo';
$return[] = 'cover';
if ( \IPS\Settings::i()->profile_comments and isset( $this->settings['allow_status_import'] ) and $this->settings['allow_status_import'] and in_array( 'user_posts', $this->authorizedScopes( $member ) ) )
if ( \IPS\Settings::i()->profile_comments and isset( $this->settings['allow_status_import'] ) and $this->settings['allow_status_import'] and ( $scopes and in_array( 'user_posts', $this->authorizedScopes( $member ) ) ) )
{
$return[] = 'status';
}
@@ -546,6 +531,22 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
/* ! Social Promotion */
/**
* Exchange a short lived member token for a longer lived token
*
* @param string $shortLivedToken The short lived token to exchange for a long lived token
* @return array
*/
public function exchangeMemberToken( $code )
{
$accessToken = $this->_exchangeAuthorizationCodeForAccessToken( $code );
/* Get user ID */
$accessToken['identifier'] = $this->authenticatedUserId( $accessToken['access_token'] );
return $accessToken;
}
/**
* Exchange a short lived token for a longer lived token
*
@@ -579,13 +580,13 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
*/
public function getPages( $member )
{
if ( !( $link = $this->_link( $member ) ) )
{
throw new \IPS\Login\Exception( $error['message'], \IPS\Login\Exception::INTERNAL_ERROR );
}
$pages = array();
$response = $this->_authorizedRequest( $link['token_identifier'] . '/accounts', $link['token_access_token'], array(
if ( !( $link = $this->_promoteLink( $member ) ) )
{
return $pages;
}
$response = $this->_authorizedRequest( $link['token_identifier'] . '/accounts?limit=100', $link['token_access_token'], array(
'appsecret_proof' => hash_hmac( 'sha256', $link['token_access_token'], $this->settings['client_secret'] )
), 'get' );
@@ -608,11 +609,11 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
*/
public function getGroups( $member )
{
if ( !( $link = $this->_link( $member ) ) )
{
throw new \IPS\Login\Exception( $error['message'], \IPS\Login\Exception::INTERNAL_ERROR );
}
$groups = array();
if ( !( $link = $this->_promoteLink( $member ) ) )
{
return $groups;
}
$response = $this->_authorizedRequest( $link['token_identifier'] . '/groups', $link['token_access_token'], array(
'appsecret_proof' => hash_hmac( 'sha256', $link['token_access_token'], $this->settings['client_secret'] )
@@ -622,11 +623,40 @@ class _Facebook extends \IPS\Login\Handler\OAuth2
{
foreach( $response['data'] as $group )
{
$groups[ $group['id'] ] = array( $group['name'], $group['privacy'] );
$groups[ $group['id'] ] = array( $group['name'], ( isset( $group['privacy'] ) ? $group['privacy'] : NULL ) );
}
}
return $groups;
}
/**
* Get user link
*
* @param \IPS\Member $member Member requesting pages
* @return array
*/
protected function _promoteLink( $member )
{
if ( \is_numeric( \IPS\Settings::i()->promote_facebook_auth ) )
{
/* standard handler */
if ( !( $link = $this->_link( $member ) ) )
{
return $pages;
}
}
else
{
$account = \IPS\core\Promote::getPromoter('Facebook')->setMember( $member );
$this->settings = json_decode( \IPS\Settings::i()->promote_facebook_auth, TRUE );
$link = array(
'token_identifier' => $account->settings['member_token']['identifier'],
'token_access_token' => $account->settings['member_token']['access_token']
);
}
return $link;
}
}
+1 -1
View File
@@ -79,7 +79,7 @@ class _Invision extends \IPS\Login\Handler\OAuth2
}
if ( !is_string( $v ) and !is_array( $v ) )
{
$$active[ $k ] = $v;
${$active}[ $k ] = $v;
}
}
+21 -9
View File
@@ -56,9 +56,9 @@ abstract class _OAuth2 extends \IPS\Login\Handler
* @endcode
*/
public function acpForm()
{
{
$return = array(
array( 'login_handler_oauth_settings', static::getTitle() . '_info' ),
array( 'login_handler_oauth_settings', \IPS\Member::loggedIn()->language()->addToStack( static::getTitle() . '_info', FALSE, array( 'sprintf' => array( (string) $this->redirectionEndpoint() ) ) ) ),
'client_id' => new \IPS\Helpers\Form\Text( 'oauth_client_id', isset( $this->settings['client_id'] ) ? $this->settings['client_id'] : NULL, TRUE ),
'client_secret' => new \IPS\Helpers\Form\Text( 'oauth_client_client_secret', isset( $this->settings['client_secret'] ) ? $this->settings['client_secret'] : NULL, NULL, array(), NULL, NULL, NULL, 'client_secret' ),
);
@@ -92,6 +92,8 @@ abstract class _OAuth2 extends \IPS\Login\Handler
*/
public function testSettings()
{
parent::testSettings();
try
{
/* Authorization Code / Implicit */
@@ -159,7 +161,7 @@ abstract class _OAuth2 extends \IPS\Login\Handler
{
$target = $this->authorizationEndpoint( $login )->setQueryString( array(
'client_id' => $this->settings['client_id'],
'response_type' => $this->grantType() === 'authorization_code' ? 'code' : 'implicit',
'response_type' => $this->grantType() === 'authorization_code' ? 'code' : 'token',
'redirect_uri' => (string) $this->redirectionEndpoint(),
'state' => $this->id . '-' . base64_encode( $login->url ) . '-' . \IPS\Session::i()->csrfKey . '-' . \IPS\Request::i()->ref,
) );
@@ -501,11 +503,21 @@ abstract class _OAuth2 extends \IPS\Login\Handler
protected function _exchangeAuthorizationCodeForAccessToken( $code )
{
/* Make the request */
$response = $this->_authenticatedRequest( $this->tokenEndpoint(), array(
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => (string) $this->redirectionEndpoint(),
) )->decodeJson();
$data = NULL;
$response = array();
try
{
$data = $this->_authenticatedRequest( $this->tokenEndpoint(), array(
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => (string) $this->redirectionEndpoint(),
) );
$response = $data->decodeJson();
}
catch( \RuntimeException $e )
{
\IPS\Log::log( var_export( $data, true ), 'oauth' );
}
/* Check for any errors */
if ( isset( $response['error'] ) or !isset( $response['access_token'] ) or ( isset( $response['token_type'] ) and mb_strtolower( $response['token_type'] ) !== 'bearer' ) )
@@ -551,7 +563,7 @@ abstract class _OAuth2 extends \IPS\Login\Handler
}
if ( isset( $newAccessToken['expires_in'] ) )
{
$update['token_expires'] = $newAccessToken['expires_in'];
$update['token_expires'] = ( time() + $newAccessToken['expires_in'] );
}
if ( isset( $newAccessToken['refresh_token'] ) )
{
+27 -6
View File
@@ -236,7 +236,7 @@ class _Wordpress extends \IPS\Login\Handler\OAuth2
{
return $this->_userData( $accessToken )[ 'username' ];
}
throw new \Exception;
return NULL;
}
/**
@@ -252,7 +252,7 @@ class _Wordpress extends \IPS\Login\Handler\OAuth2
{
return $this->_userData( $accessToken )[ 'display_name' ];
}
throw new \Exception;
return NULL;
}
/**
@@ -268,7 +268,7 @@ class _Wordpress extends \IPS\Login\Handler\OAuth2
{
return $this->_userData( $accessToken )[ 'email' ];
}
throw new \Exception;
return NULL;
}
/**
@@ -287,10 +287,31 @@ class _Wordpress extends \IPS\Login\Handler\OAuth2
{
throw new \IPS\Login\Exception( NULL, \IPS\Login\Exception::INTERNAL_ERROR );
}
if ( isset( $this->_userData( $link['token_access_token'] )[ 'avatar' ] ) )
{
return $this->_userData( $link['token_access_token'] )[ 'avatar' ];
{
$url = preg_replace( '/^.*src=[\'"](.+?)[\'"].*$/i', '$1', $this->_userData( $link['token_access_token'] )[ 'avatar' ] );
if ( preg_match( '/^https?:\/\/(?:secure\.|www\.)?gravatar\.com\/avatar\/(.{32})/', $url, $matches ) )
{
$url = \IPS\Http\Url::external( "https://secure.gravatar.com/avatar/" . $matches[1] )->setQueryString( 'd', '404' );
try
{
$response = $url->request()->head();
if ( $response->httpResponseCode == 200 )
{
return $url;
}
}
catch ( \Exception $e ) { }
}
else
{
try
{
return \IPS\Http\Url::external( $url );
}
catch ( \Exception $e ) { }
}
}
return NULL;
}