2016-09-03 01:54:22 +03:00
< ? php
2019-12-05 01:15:45 +03:00
declare ( strict_types = 1 );
2016-09-03 01:54:22 +03:00
namespace api\modules\session\models ;
use api\modules\session\exceptions\ForbiddenOperationException ;
use api\modules\session\exceptions\IllegalArgumentException ;
2016-09-05 17:55:38 +03:00
use api\modules\session\models\protocols\JoinInterface ;
2016-09-03 01:54:22 +03:00
use api\modules\session\Module as Session ;
use api\modules\session\validators\RequiredValidator ;
2019-08-02 03:29:20 +03:00
use api\rbac\Permissions as P ;
2016-09-05 17:55:38 +03:00
use common\helpers\StringHelper ;
2016-09-03 01:54:22 +03:00
use common\models\Account ;
use common\models\MinecraftAccessKey ;
2017-09-19 20:06:16 +03:00
use Ramsey\Uuid\Uuid ;
2019-12-05 01:15:45 +03:00
use Webmozart\Assert\Assert ;
2016-09-03 01:54:22 +03:00
use Yii ;
2016-09-05 17:55:38 +03:00
use yii\base\Model ;
2016-09-03 01:54:22 +03:00
use yii\web\UnauthorizedHttpException ;
2016-09-05 17:55:38 +03:00
class JoinForm extends Model {
2016-09-03 01:54:22 +03:00
2016-09-06 12:56:39 +03:00
public $accessToken ;
2018-04-17 23:47:25 +03:00
2016-09-06 12:56:39 +03:00
public $selectedProfile ;
2018-04-17 23:47:25 +03:00
2016-09-06 12:56:39 +03:00
public $serverId ;
2016-09-03 01:54:22 +03:00
2016-09-05 17:55:38 +03:00
/**
* @ var Account | null
*/
2016-09-03 01:54:22 +03:00
private $account ;
2016-09-05 17:55:38 +03:00
/**
* @ var JoinInterface
*/
private $protocol ;
public function __construct ( JoinInterface $protocol , array $config = []) {
2019-12-05 01:15:45 +03:00
parent :: __construct ( $config );
2016-09-05 17:55:38 +03:00
$this -> protocol = $protocol ;
$this -> accessToken = $protocol -> getAccessToken ();
$this -> selectedProfile = $protocol -> getSelectedProfile ();
$this -> serverId = $protocol -> getServerId ();
}
2019-12-05 01:15:45 +03:00
public function rules () : array {
2016-09-03 01:54:22 +03:00
return [
2016-09-05 17:55:38 +03:00
[[ 'accessToken' , 'serverId' ], RequiredValidator :: class ],
2016-09-03 01:54:22 +03:00
[[ 'accessToken' , 'selectedProfile' ], 'validateUuid' ],
[[ 'accessToken' ], 'validateAccessToken' ],
];
}
2019-12-05 01:15:45 +03:00
/**
* @ return bool
* @ throws IllegalArgumentException
* @ throws ForbiddenOperationException
*/
public function join () : bool {
2016-09-05 17:55:38 +03:00
$serverId = $this -> serverId ;
$accessToken = $this -> accessToken ;
Session :: info ( " User with access_token = ' { $accessToken } ' trying join to server with server_id = ' { $serverId } '. " );
2018-01-02 20:45:04 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.join.attempt' );
2016-09-03 01:54:22 +03:00
if ( ! $this -> validate ()) {
return false ;
}
$account = $this -> getAccount ();
2016-09-05 17:55:38 +03:00
$sessionModel = new SessionModel ( $account -> username , $serverId );
2019-12-05 01:15:45 +03:00
Assert :: true ( $sessionModel -> save ());
2016-09-03 01:54:22 +03:00
2017-11-19 15:36:51 +03:00
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 01:54:22 +03:00
return true ;
}
2019-12-05 01:15:45 +03:00
/**
* @ param string $attributeNames
* @ param bool $clearErrors
*
* @ return bool
* @ throws IllegalArgumentException
*/
public function validate ( $attributeNames = null , $clearErrors = true ) : bool {
2016-09-05 17:55:38 +03:00
if ( ! $this -> protocol -> validate ()) {
throw new IllegalArgumentException ();
}
return parent :: validate ( $attributeNames , $clearErrors );
}
2019-12-05 01:15:45 +03:00
/**
* @ param string $attribute
*
* @ throws IllegalArgumentException
*/
public function validateUuid ( string $attribute ) : void {
2016-09-03 01:54:22 +03:00
if ( $this -> hasErrors ( $attribute )) {
return ;
}
2017-09-19 20:06:16 +03:00
if ( $this -> $attribute === Uuid :: NIL ) {
2016-09-03 01:54:22 +03:00
throw new IllegalArgumentException ();
}
}
/**
2019-12-05 01:15:45 +03:00
* @ throws \api\modules\session\exceptions\ForbiddenOperationException
2016-09-03 01:54:22 +03:00
*/
2019-12-05 01:15:45 +03:00
public function validateAccessToken () : void {
2016-09-03 01:54:22 +03:00
$accessToken = $this -> accessToken ;
/** @var MinecraftAccessKey|null $accessModel */
2019-12-05 01:15:45 +03:00
$accessModel = MinecraftAccessKey :: findOne ([ 'access_token' => $accessToken ]);
2017-09-19 20:06:16 +03:00
if ( $accessModel !== null ) {
2017-11-21 20:11:28 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.legacy_minecraft_protocol' );
2017-09-19 20:06:16 +03:00
/** @var MinecraftAccessKey|\api\components\OAuth2\Entities\AccessTokenEntity $accessModel */
if ( $accessModel -> isExpired ()) {
Session :: error ( " User with access_token = ' { $accessToken } ' failed join by expired access_token. " );
2017-11-21 20:11:28 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.legacy_minecraft_protocol_token_expired' );
2019-12-05 01:15:45 +03:00
2017-09-19 20:06:16 +03:00
throw new ForbiddenOperationException ( 'Expired access_token.' );
}
$account = $accessModel -> account ;
} else {
2016-09-03 01:54:22 +03:00
try {
2017-09-19 20:06:16 +03:00
$identity = Yii :: $app -> user -> loginByAccessToken ( $accessToken );
2016-09-03 01:54:22 +03:00
} catch ( UnauthorizedHttpException $e ) {
$identity = null ;
}
if ( $identity === null ) {
Session :: error ( " User with access_token = ' { $accessToken } ' failed join by wrong access_token. " );
2017-11-19 15:36:51 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.join.fail_wrong_token' );
2019-12-05 01:15:45 +03:00
2016-09-03 01:54:22 +03:00
throw new ForbiddenOperationException ( 'Invalid access_token.' );
}
2017-11-21 20:11:28 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2' );
2017-09-19 20:06:16 +03:00
if ( ! Yii :: $app -> user -> can ( P :: MINECRAFT_SERVER_SESSION )) {
2016-09-03 01:54:22 +03:00
Session :: error ( " User with access_token = ' { $accessToken } ' doesn't have enough scopes to make join. " );
2017-11-21 20:11:28 +03:00
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2_not_enough_scopes' );
2019-12-05 01:15:45 +03:00
2016-09-03 01:54:22 +03:00
throw new ForbiddenOperationException ( 'The token does not have required scope.' );
}
$account = $identity -> getAccount ();
}
2016-09-05 17:55:38 +03:00
$selectedProfile = $this -> selectedProfile ;
$isUuid = StringHelper :: isUuid ( $selectedProfile );
2017-10-20 15:02:52 +03:00
if ( $isUuid && $account -> uuid !== $this -> normalizeUUID ( $selectedProfile )) {
2017-11-19 15:36:51 +03:00
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 01:15:45 +03:00
2016-09-03 01:54:22 +03:00
throw new ForbiddenOperationException ( 'Wrong selected_profile.' );
2017-09-19 20:06:16 +03:00
}
2017-10-20 15:19:39 +03:00
if ( ! $isUuid && mb_strtolower ( $account -> username ) !== mb_strtolower ( $selectedProfile )) {
2017-11-19 15:36:51 +03:00
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 01:15:45 +03:00
2016-09-05 17:55:38 +03:00
throw new ForbiddenOperationException ( 'Invalid credentials' );
2016-09-03 01:54:22 +03:00
}
2020-06-12 00:27:02 +03:00
if ( $account -> status === Account :: STATUS_DELETED ) {
throw new ForbiddenOperationException ( 'Invalid credentials' );
}
2016-09-03 01:54:22 +03:00
$this -> account = $account ;
}
2017-09-19 20:06:16 +03:00
protected function getAccount () : Account {
2016-09-03 01:54:22 +03:00
return $this -> account ;
}
2017-10-20 15:02:52 +03:00
private function normalizeUUID ( string $uuid ) : string {
return Uuid :: fromString ( $uuid ) -> toString ();
}
2016-09-03 01:54:22 +03:00
}