2016-09-03 04:24:22 +05:30
< ? php
2019-12-05 03:45:45 +05:30
declare ( strict_types = 1 );
2016-09-03 04:24:22 +05:30
namespace api\modules\session\models ;
use api\modules\session\exceptions\ForbiddenOperationException ;
use api\modules\session\exceptions\IllegalArgumentException ;
2016-09-05 20:25:38 +05:30
use api\modules\session\models\protocols\JoinInterface ;
2016-09-03 04:24:22 +05:30
use api\modules\session\Module as Session ;
use api\modules\session\validators\RequiredValidator ;
2019-08-02 05:59:20 +05:30
use api\rbac\Permissions as P ;
2024-06-14 09:12:35 +05:30
use Closure ;
2016-09-05 20:25:38 +05:30
use common\helpers\StringHelper ;
2016-09-03 04:24:22 +05:30
use common\models\Account ;
2017-09-19 22:36:16 +05:30
use Ramsey\Uuid\Uuid ;
2019-12-05 03:45:45 +05:30
use Webmozart\Assert\Assert ;
2016-09-03 04:24:22 +05:30
use Yii ;
2016-09-05 20:25:38 +05:30
use yii\base\Model ;
2016-09-03 04:24:22 +05:30
use yii\web\UnauthorizedHttpException ;
2016-09-05 20:25:38 +05:30
class JoinForm extends Model {
2016-09-03 04:24:22 +05:30
2016-09-06 15:26:39 +05:30
public $accessToken ;
2018-04-18 02:17:25 +05:30
2016-09-06 15:26:39 +05:30
public $selectedProfile ;
2018-04-18 02:17:25 +05:30
2016-09-06 15:26:39 +05:30
public $serverId ;
2016-09-03 04:24:22 +05:30
2016-09-05 20:25:38 +05:30
/**
* @ var Account | null
*/
2016-09-03 04:24:22 +05:30
private $account ;
2016-09-05 20:25:38 +05:30
/**
* @ var JoinInterface
*/
private $protocol ;
public function __construct ( JoinInterface $protocol , array $config = []) {
2019-12-05 03:45:45 +05:30
parent :: __construct ( $config );
2016-09-05 20:25:38 +05:30
$this -> protocol = $protocol ;
$this -> accessToken = $protocol -> getAccessToken ();
$this -> selectedProfile = $protocol -> getSelectedProfile ();
$this -> serverId = $protocol -> getServerId ();
}
2019-12-05 03:45:45 +05:30
public function rules () : array {
2016-09-03 04:24:22 +05:30
return [
2016-09-05 20:25:38 +05:30
[[ 'accessToken' , 'serverId' ], RequiredValidator :: class ],
2024-06-14 09:12:35 +05:30
[[ 'accessToken' , 'selectedProfile' ], Closure :: fromCallable ([ $this , 'validateUuid' ])],
[[ 'accessToken' ], Closure :: fromCallable ([ $this , 'validateAccessToken' ])],
2016-09-03 04:24:22 +05:30
];
}
2019-12-05 03:45:45 +05:30
/**
* @ throws IllegalArgumentException
* @ throws ForbiddenOperationException
*/
public function join () : bool {
2016-09-05 20:25:38 +05:30
$serverId = $this -> serverId ;
$accessToken = $this -> accessToken ;
Session :: info ( " User with access_token = ' { $accessToken } ' trying join to server with server_id = ' { $serverId } '. " );
2018-01-02 23:15:04 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.join.attempt' );
2016-09-03 04:24:22 +05:30
if ( ! $this -> validate ()) {
return false ;
}
2024-06-14 09:12:35 +05:30
$account = $this -> account ;
2016-09-05 20:25:38 +05:30
$sessionModel = new SessionModel ( $account -> username , $serverId );
2019-12-05 03:45:45 +05:30
Assert :: true ( $sessionModel -> save ());
2016-09-03 04:24:22 +05:30
2017-11-19 18:06:51 +05:30
Session :: info ( " User with access_token = ' { $accessToken } ' and nickname = ' { $account -> username } ' successfully joined to server_id = ' { $serverId } '. " );
Yii :: $app -> statsd -> inc ( 'sessionserver.join.success' );
2016-09-03 04:24:22 +05:30
return true ;
}
2019-12-05 03:45:45 +05:30
/**
* @ param string $attributeNames
* @ param bool $clearErrors
*
* @ return bool
* @ throws IllegalArgumentException
*/
public function validate ( $attributeNames = null , $clearErrors = true ) : bool {
2016-09-05 20:25:38 +05:30
if ( ! $this -> protocol -> validate ()) {
throw new IllegalArgumentException ();
}
return parent :: validate ( $attributeNames , $clearErrors );
}
2019-12-05 03:45:45 +05:30
/**
* @ param string $attribute
*
* @ throws IllegalArgumentException
*/
2024-06-14 09:12:35 +05:30
private function validateUuid ( string $attribute ) : void {
2016-09-03 04:24:22 +05:30
if ( $this -> hasErrors ( $attribute )) {
return ;
}
2017-09-19 22:36:16 +05:30
if ( $this -> $attribute === Uuid :: NIL ) {
2016-09-03 04:24:22 +05:30
throw new IllegalArgumentException ();
}
}
/**
2019-12-05 03:45:45 +05:30
* @ throws \api\modules\session\exceptions\ForbiddenOperationException
2016-09-03 04:24:22 +05:30
*/
2024-06-14 09:12:35 +05:30
private function validateAccessToken () : void {
2016-09-03 04:24:22 +05:30
$accessToken = $this -> accessToken ;
2024-06-14 09:12:35 +05:30
try {
$identity = Yii :: $app -> user -> loginByAccessToken ( $accessToken );
} catch ( UnauthorizedHttpException $e ) {
if ( $e -> getMessage () === 'Token expired' ) {
throw new ForbiddenOperationException ( 'Expired access_token.' , 0 , $e );
2016-09-03 04:24:22 +05:30
}
2024-06-14 09:12:35 +05:30
$identity = null ;
}
2019-12-05 03:45:45 +05:30
2024-06-14 09:12:35 +05:30
if ( $identity === null ) {
Session :: error ( " User with access_token = ' { $accessToken } ' failed join by wrong access_token. " );
Yii :: $app -> statsd -> inc ( 'sessionserver.join.fail_wrong_token' );
2016-09-03 04:24:22 +05:30
2024-06-14 09:12:35 +05:30
throw new ForbiddenOperationException ( 'Invalid access_token.' );
}
2019-12-05 03:45:45 +05:30
2024-06-14 09:12:35 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2' );
if ( ! Yii :: $app -> user -> can ( P :: MINECRAFT_SERVER_SESSION )) {
Session :: error ( " User with access_token = ' { $accessToken } ' doesn't have enough scopes to make join. " );
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2_not_enough_scopes' );
2016-09-03 04:24:22 +05:30
2024-06-14 09:12:35 +05:30
throw new ForbiddenOperationException ( 'The token does not have required scope.' );
2016-09-03 04:24:22 +05:30
}
2024-06-14 09:12:35 +05:30
/** @var Account $account */
$account = $identity -> getAccount ();
2016-09-05 20:25:38 +05:30
$selectedProfile = $this -> selectedProfile ;
$isUuid = StringHelper :: isUuid ( $selectedProfile );
2017-10-20 17:32:52 +05:30
if ( $isUuid && $account -> uuid !== $this -> normalizeUUID ( $selectedProfile )) {
2017-11-19 18:06:51 +05:30
Session :: error ( " User with access_token = ' { $accessToken } ' trying to join with identity = ' { $selectedProfile } ', but access_token issued to account with id = ' { $account -> uuid } '. " );
Yii :: $app -> statsd -> inc ( 'sessionserver.join.fail_uuid_mismatch' );
2019-12-05 03:45:45 +05:30
2016-09-03 04:24:22 +05:30
throw new ForbiddenOperationException ( 'Wrong selected_profile.' );
2017-09-19 22:36:16 +05:30
}
2017-10-20 17:49:39 +05:30
if ( ! $isUuid && mb_strtolower ( $account -> username ) !== mb_strtolower ( $selectedProfile )) {
2017-11-19 18:06:51 +05:30
Session :: error ( " User with access_token = ' { $accessToken } ' trying to join with identity = ' { $selectedProfile } ', but access_token issued to account with username = ' { $account -> username } '. " );
Yii :: $app -> statsd -> inc ( 'sessionserver.join.fail_username_mismatch' );
2019-12-05 03:45:45 +05:30
2016-09-05 20:25:38 +05:30
throw new ForbiddenOperationException ( 'Invalid credentials' );
2016-09-03 04:24:22 +05:30
}
2020-06-12 02:57:02 +05:30
if ( $account -> status === Account :: STATUS_DELETED ) {
throw new ForbiddenOperationException ( 'Invalid credentials' );
}
2016-09-03 04:24:22 +05:30
$this -> account = $account ;
}
2017-10-20 17:32:52 +05:30
private function normalizeUUID ( string $uuid ) : string {
return Uuid :: fromString ( $uuid ) -> toString ();
}
2016-09-03 04:24:22 +05:30
}