2016-09-03 04:24:22 +05:30
< ? php
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 ;
2016-09-05 20:25:38 +05:30
use common\helpers\StringHelper ;
2016-09-03 04:24:22 +05:30
use common\models\Account ;
use common\models\MinecraftAccessKey ;
2018-04-18 02:17:25 +05:30
use common\rbac\Permissions as P ;
2017-09-19 22:36:16 +05:30
use Ramsey\Uuid\Uuid ;
2016-09-03 04:24:22 +05:30
use Yii ;
use yii\base\ErrorException ;
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 = []) {
$this -> protocol = $protocol ;
$this -> accessToken = $protocol -> getAccessToken ();
$this -> selectedProfile = $protocol -> getSelectedProfile ();
$this -> serverId = $protocol -> getServerId ();
parent :: __construct ( $config );
}
2016-09-03 04:24:22 +05:30
public function rules () {
return [
2016-09-05 20:25:38 +05:30
[[ 'accessToken' , 'serverId' ], RequiredValidator :: class ],
2016-09-03 04:24:22 +05:30
[[ 'accessToken' , 'selectedProfile' ], 'validateUuid' ],
[[ 'accessToken' ], 'validateAccessToken' ],
];
}
public function join () {
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 ;
}
$account = $this -> getAccount ();
2016-09-05 20:25:38 +05:30
$sessionModel = new SessionModel ( $account -> username , $serverId );
2016-09-03 04:24:22 +05:30
if ( ! $sessionModel -> save ()) {
throw new ErrorException ( 'Cannot save join session model' );
}
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 ;
}
2016-09-05 20:25:38 +05:30
public function validate ( $attributeNames = null , $clearErrors = true ) {
if ( ! $this -> protocol -> validate ()) {
throw new IllegalArgumentException ();
}
return parent :: validate ( $attributeNames , $clearErrors );
}
2016-09-03 04:24:22 +05:30
public function validateUuid ( $attribute ) {
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 ();
}
}
/**
* @ throws \api\modules\session\exceptions\SessionServerException
*/
public function validateAccessToken () {
$accessToken = $this -> accessToken ;
/** @var MinecraftAccessKey|null $accessModel */
$accessModel = MinecraftAccessKey :: findOne ( $accessToken );
2017-09-19 22:36:16 +05:30
if ( $accessModel !== null ) {
2017-11-21 22:41:28 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.legacy_minecraft_protocol' );
2017-09-19 22:36:16 +05:30
/** @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 22:41:28 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.legacy_minecraft_protocol_token_expired' );
2017-09-19 22:36:16 +05:30
throw new ForbiddenOperationException ( 'Expired access_token.' );
}
$account = $accessModel -> account ;
} else {
2016-09-03 04:24:22 +05:30
try {
2017-09-19 22:36:16 +05:30
$identity = Yii :: $app -> user -> loginByAccessToken ( $accessToken );
2016-09-03 04:24:22 +05:30
} catch ( UnauthorizedHttpException $e ) {
$identity = null ;
}
if ( $identity === null ) {
Session :: error ( " User with access_token = ' { $accessToken } ' failed join by wrong access_token. " );
2017-11-19 18:06:51 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.join.fail_wrong_token' );
2016-09-03 04:24:22 +05:30
throw new ForbiddenOperationException ( 'Invalid access_token.' );
}
2017-11-21 22:41:28 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2' );
2017-09-19 22:36:16 +05:30
if ( ! Yii :: $app -> user -> can ( P :: MINECRAFT_SERVER_SESSION )) {
2016-09-03 04:24:22 +05:30
Session :: error ( " User with access_token = ' { $accessToken } ' doesn't have enough scopes to make join. " );
2017-11-21 22:41:28 +05:30
Yii :: $app -> statsd -> inc ( 'sessionserver.authentication.oauth2_not_enough_scopes' );
2016-09-03 04:24:22 +05:30
throw new ForbiddenOperationException ( 'The token does not have required scope.' );
}
$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' );
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' );
2016-09-05 20:25:38 +05:30
throw new ForbiddenOperationException ( 'Invalid credentials' );
2016-09-03 04:24:22 +05:30
}
$this -> account = $account ;
}
2017-09-19 22:36:16 +05:30
protected function getAccount () : Account {
2016-09-03 04:24:22 +05:30
return $this -> 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
}