Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
+60
-51
@@ -7,7 +7,17 @@
|
||||
* @package Invision Community
|
||||
* @since 29 Apr 2017
|
||||
*/
|
||||
\define('REPORT_EXCEPTIONS', TRUE);
|
||||
|
||||
use IPS\Api\OAuthClient;
|
||||
use IPS\Db;
|
||||
use IPS\Login;
|
||||
use IPS\Member;
|
||||
use IPS\Member\Device;
|
||||
use IPS\Output;
|
||||
use IPS\Request;
|
||||
use const IPS\OAUTH_REQUIRES_HTTPS;
|
||||
|
||||
define('REPORT_EXCEPTIONS', TRUE);
|
||||
require '../../init.php';
|
||||
|
||||
class oAuthServerTokenRequest
|
||||
@@ -31,13 +41,13 @@ class oAuthServerTokenRequest
|
||||
/* Get the client */
|
||||
try
|
||||
{
|
||||
$obj->client = \IPS\Api\OAuthClient::load( $clientId );
|
||||
$obj->client = OAuthClient::load( $clientId );
|
||||
if ( !$obj->client->enabled )
|
||||
{
|
||||
throw new \OutOfRangeException;
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
}
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch (OutOfRangeException $e )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client' );
|
||||
}
|
||||
@@ -47,27 +57,27 @@ class oAuthServerTokenRequest
|
||||
{
|
||||
$bruteForce = $obj->client->brute_force ? json_decode( $obj->client->brute_force, TRUE ) : array();
|
||||
|
||||
if ( isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) and $bruteForce[ \IPS\Request::i()->ipAddress() ] >= 3 )
|
||||
if ( isset( $bruteForce[ Request::i()->ipAddress() ] ) and $bruteForce[ Request::i()->ipAddress() ] >= 3 )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client', "blocked for too many authentication failures" );
|
||||
}
|
||||
|
||||
if ( password_verify( $clientSecret, $obj->client->client_secret ) )
|
||||
{
|
||||
if ( isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) )
|
||||
if ( isset( $bruteForce[ Request::i()->ipAddress() ] ) )
|
||||
{
|
||||
unset( $bruteForce[ \IPS\Request::i()->ipAddress() ] );
|
||||
unset( $bruteForce[ Request::i()->ipAddress() ] );
|
||||
$obj->client->brute_force = json_encode( $bruteForce );
|
||||
$obj->client->save();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !isset( $bruteForce[ \IPS\Request::i()->ipAddress() ] ) )
|
||||
if ( !isset( $bruteForce[ Request::i()->ipAddress() ] ) )
|
||||
{
|
||||
$bruteForce[ \IPS\Request::i()->ipAddress() ] = 0;
|
||||
$bruteForce[ Request::i()->ipAddress() ] = 0;
|
||||
}
|
||||
$bruteForce[ \IPS\Request::i()->ipAddress() ]++;
|
||||
$bruteForce[ Request::i()->ipAddress() ]++;
|
||||
$obj->client->brute_force = json_encode( $bruteForce );
|
||||
$obj->client->save();
|
||||
|
||||
@@ -86,7 +96,7 @@ class oAuthServerTokenRequest
|
||||
*/
|
||||
public function grantType( $grantType )
|
||||
{
|
||||
if ( !\in_array( $grantType, array( 'authorization_code', 'implicit', 'client_credentials', 'password', 'refresh_token' ) ) )
|
||||
if ( !in_array( $grantType, array( 'authorization_code', 'implicit', 'client_credentials', 'password', 'refresh_token' ) ) )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'unsupported_grant_type' );
|
||||
}
|
||||
@@ -98,7 +108,7 @@ class oAuthServerTokenRequest
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'unsupported_grant_type' );
|
||||
}
|
||||
}
|
||||
elseif ( !\in_array( $grantType, explode( ',', $this->client->grant_types ) ) )
|
||||
elseif ( !in_array( $grantType, explode( ',', $this->client->grant_types ) ) )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'unauthorized_client' );
|
||||
}
|
||||
@@ -119,19 +129,19 @@ class oAuthServerTokenRequest
|
||||
{
|
||||
try
|
||||
{
|
||||
$authorizationCode = \IPS\Db::i()->select( '*', 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $this->client->client_id, $authorizationCode ) )->first();
|
||||
$authorizationCode = Db::i()->select( '*', 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $this->client->client_id, $authorizationCode ) )->first();
|
||||
|
||||
/* If it's expired, delete it and do not validate */
|
||||
if ( $authorizationCode['expires'] < time() )
|
||||
{
|
||||
\IPS\Db::i()->delete( 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
|
||||
Db::i()->delete( 'core_oauth_server_authorization_codes', array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
|
||||
return;
|
||||
}
|
||||
|
||||
/* If it's already been used, this should be treated as an attack: revoke any access tokens already generated and do not validate */
|
||||
if ( $authorizationCode['used'] )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'client_id=? AND authorization_code=?', $this->client->client_id, $authorizationCode['code'] ) );
|
||||
Db::i()->update( 'core_oauth_server_access_tokens', array( 'status' => 'revoked' ), array( 'client_id=? AND authorization_code=?', $this->client->client_id, $authorizationCode['code'] ) );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,34 +164,34 @@ class oAuthServerTokenRequest
|
||||
$codeVerifier = rtrim( strtr( base64_encode( pack( 'H*', hash( 'sha256', $codeVerifier ) ) ), '+/', '-_' ), '=' );
|
||||
}
|
||||
|
||||
if ( !\IPS\Login::compareHashes( $authorizationCode['code_challenge'], $codeVerifier ) )
|
||||
if ( !Login::compareHashes( $authorizationCode['code_challenge'], $codeVerifier ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check we're not banned and not validating */
|
||||
$member = \IPS\Member::load( $authorizationCode['member_id'] );
|
||||
$member = Member::load( $authorizationCode['member_id'] );
|
||||
if ( $member->isBanned() or $member->members_bitoptions['validating'] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark it used */
|
||||
\IPS\Db::i()->update( 'core_oauth_server_authorization_codes', array( 'used' => 1 ), array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
|
||||
Db::i()->update( 'core_oauth_server_authorization_codes', array( 'used' => 1 ), array( 'client_id=? AND code=?', $authorizationCode['client_id'], $authorizationCode['code'] ) );
|
||||
|
||||
/* Return access token */
|
||||
try
|
||||
{
|
||||
$device = $authorizationCode['device_key'] ? \IPS\Member\Device::load( $authorizationCode['device_key'] ) : NULL;
|
||||
$device = $authorizationCode['device_key'] ? Device::load( $authorizationCode['device_key'] ) : NULL;
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch (UnderflowException $e )
|
||||
{
|
||||
$device = NULL;
|
||||
}
|
||||
return $this->client->generateAccessToken( $member, $authorizationCode['scope'] ? json_decode( $authorizationCode['scope'] ) : NULL, 'authorization_code', FALSE, $authorizationCode['code'], $authorizationCode['user_agent'], isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : NULL, $device );
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch (UnderflowException $e )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -197,21 +207,18 @@ class oAuthServerTokenRequest
|
||||
*/
|
||||
public function validatePassword( $username, $password, $scope )
|
||||
{
|
||||
$member = NULL;
|
||||
$accessToken = NULL;
|
||||
$fails = [];
|
||||
$success = FALSE;
|
||||
$fails = array();
|
||||
|
||||
$login = new \IPS\Login();
|
||||
$login = new Login();
|
||||
|
||||
foreach ( $login->usernamePasswordMethods() as $method )
|
||||
{
|
||||
try
|
||||
{
|
||||
if( $member = $method->authenticateUsernamePassword( $login, $username, $password ) )
|
||||
{
|
||||
$success = TRUE;
|
||||
}
|
||||
\IPS\Login::checkIfAccountIsLocked( $member, TRUE );
|
||||
$member = $method->authenticateUsernamePassword( $login, $username, $password );
|
||||
Login::checkIfAccountIsLocked( $member, TRUE );
|
||||
|
||||
if ( !$member->isBanned() and !$member->members_bitoptions['validating'] )
|
||||
{
|
||||
@@ -223,21 +230,23 @@ class oAuthServerTokenRequest
|
||||
{
|
||||
if ( $e->getCode() === \IPS\Login\Exception::BAD_PASSWORD and $e->member )
|
||||
{
|
||||
$fails[ $e->member->member_id ] = $e->member;
|
||||
$e->member->failedLogin();
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
catch (Exception $e ) { }
|
||||
}
|
||||
|
||||
/* Record any fails if none of the sign-ins were successful */
|
||||
if( $success === TRUE )
|
||||
|
||||
foreach ( $fails as $failedMember )
|
||||
{
|
||||
foreach ( $fails as $failedMember )
|
||||
if ( !$member or $failedMember->member_id != $failedMember->member_id )
|
||||
{
|
||||
$failedMember->failedLogin();
|
||||
$failedLogins = is_array( $failedMember->failed_logins ) ? $failedMember->failed_logins : array();
|
||||
$failedLogins[ Request::i()->ipAddress() ][] = time();
|
||||
$failedMember->failed_logins = $failedLogins;
|
||||
$failedMember->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
@@ -270,7 +279,7 @@ class oAuthServerTokenRequest
|
||||
$member = NULL;
|
||||
if ( $accessToken['member_id'] )
|
||||
{
|
||||
$member = \IPS\Member::load( $accessToken['member_id'] );
|
||||
$member = Member::load( $accessToken['member_id'] );
|
||||
if ( !$member->member_id or $member->isBanned() or $member->members_bitoptions['validating'] )
|
||||
{
|
||||
return;
|
||||
@@ -282,9 +291,9 @@ class oAuthServerTokenRequest
|
||||
|
||||
try
|
||||
{
|
||||
$device = $accessToken['device_key'] ? \IPS\Member\Device::load( $accessToken['device_key'] ) : NULL;
|
||||
$device = $accessToken['device_key'] ? Device::load( $accessToken['device_key'] ) : NULL;
|
||||
}
|
||||
catch ( \UnderflowException $e )
|
||||
catch (UnderflowException $e )
|
||||
{
|
||||
$device = NULL;
|
||||
}
|
||||
@@ -298,19 +307,19 @@ class oAuthServerTokenRequest
|
||||
try
|
||||
{
|
||||
/* TLS only */
|
||||
if ( \IPS\OAUTH_REQUIRES_HTTPS and !\IPS\Request::i()->isSecure() )
|
||||
if ( OAUTH_REQUIRES_HTTPS and !Request::i()->isSecure() )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "request must be made with https" );
|
||||
}
|
||||
|
||||
/* POST only */
|
||||
if ( \IPS\Request::i()->requestMethod() !== 'POST' )
|
||||
if ( Request::i()->requestMethod() !== 'POST' )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_request', "request must be a POST request" );
|
||||
}
|
||||
|
||||
/* Check we are not IP banned */
|
||||
$ipBanned = \IPS\Request::i()->ipAddressIsBanned();
|
||||
$ipBanned = Request::i()->ipAddressIsBanned();
|
||||
if ( $ipBanned )
|
||||
{
|
||||
throw new \IPS\Login\Handler\OAuth2\Exception( 'invalid_client', "IP Address banned" );
|
||||
@@ -350,19 +359,19 @@ try
|
||||
|
||||
/* Validate grant */
|
||||
$accessToken = NULL;
|
||||
switch ( $request->grantType( \IPS\Request::i()->grant_type ) )
|
||||
switch ( $request->grantType( Request::i()->grant_type ) )
|
||||
{
|
||||
case 'authorization_code':
|
||||
$accessToken = $request->validateAuthorizationCode( \IPS\Request::i()->code, \IPS\Request::i()->redirect_uri, isset( \IPS\Request::i()->code_verifier ) ? \IPS\Request::i()->code_verifier : NULL );
|
||||
$accessToken = $request->validateAuthorizationCode( Request::i()->code, Request::i()->redirect_uri, isset( Request::i()->code_verifier ) ? Request::i()->code_verifier : NULL );
|
||||
break;
|
||||
case 'password':
|
||||
$accessToken = $request->validatePassword( \IPS\Request::i()->username, \IPS\Request::i()->protect('password'), isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
|
||||
$accessToken = $request->validatePassword( Request::i()->username, Request::i()->protect('password'), isset( Request::i()->scope ) ? explode( ' ', Request::i()->scope ) : NULL );
|
||||
break;
|
||||
case 'client_credentials':
|
||||
$accessToken = $request->validateClientCredentials( isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
|
||||
$accessToken = $request->validateClientCredentials( isset( Request::i()->scope ) ? explode( ' ', Request::i()->scope ) : NULL );
|
||||
break;
|
||||
case 'refresh_token':
|
||||
$accessToken = $request->validateRefreshToken( \IPS\Request::i()->refresh_token, isset( \IPS\Request::i()->scope ) ? explode( ' ', \IPS\Request::i()->scope ) : NULL );
|
||||
$accessToken = $request->validateRefreshToken( Request::i()->refresh_token, isset( Request::i()->scope ) ? explode( ' ', Request::i()->scope ) : NULL );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -383,7 +392,7 @@ try
|
||||
$response['scope'] = implode( ' ', json_decode( $accessToken['scope'], TRUE ) );
|
||||
}
|
||||
|
||||
\IPS\Output::i()->sendOutput( json_encode( $response ), 200, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', 'Pragma' => 'no-cache' ), FALSE, FALSE, FALSE );
|
||||
Output::i()->sendOutput( json_encode( $response ), 200, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0', 'Pragma' => 'no-cache' ), FALSE, FALSE, FALSE );
|
||||
|
||||
}
|
||||
else
|
||||
@@ -398,9 +407,9 @@ catch ( \IPS\Login\Handler\OAuth2\Exception $e )
|
||||
{
|
||||
$response['error_description'] = $e->description;
|
||||
}
|
||||
\IPS\Output::i()->sendOutput( json_encode( $response ), $e->getMessage() === 'invalid_client' ? 401 : 400, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' ), FALSE, FALSE, FALSE );
|
||||
Output::i()->sendOutput( json_encode( $response ), $e->getMessage() === 'invalid_client' ? 401 : 400, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
catch ( Exception $e )
|
||||
{
|
||||
\IPS\Output::i()->sendOutput( json_encode( array( 'error' => 'server_error', 'error_description' => $e->getMessage() ) ), 500, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' ), FALSE, FALSE, FALSE );
|
||||
Output::i()->sendOutput( json_encode( array( 'error' => 'server_error', 'error_description' => $e->getMessage() ) ), 500, 'application/json', array( 'Cache-Control' => 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0' ), FALSE, FALSE, FALSE );
|
||||
}
|
||||
Reference in new issue
Block a user