diff --git a/api/modules/session/controllers/SessionController.php b/api/modules/session/controllers/SessionController.php index 6e88b11..4e1fc34 100644 --- a/api/modules/session/controllers/SessionController.php +++ b/api/modules/session/controllers/SessionController.php @@ -1,4 +1,6 @@ response->format = Response::FORMAT_JSON; $data = Yii::$app->request->post(); $protocol = new ModernJoin($data['accessToken'] ?? '', $data['selectedProfile'] ?? '', $data['serverId'] ?? ''); $joinForm = new JoinForm($protocol); - $joinForm->join(); + $joinForm->join(); // will throw an exception in case of any error return ['id' => 'OK']; } - public function actionJoinLegacy() { + public function actionJoinLegacy(): string { Yii::$app->response->format = Response::FORMAT_RAW; $data = Yii::$app->request->get(); @@ -64,7 +71,12 @@ class SessionController extends Controller { return 'OK'; } - public function actionHasJoined() { + /** + * @return array + * @throws ForbiddenOperationException + * @throws IllegalArgumentException + */ + public function actionHasJoined(): array { Yii::$app->response->format = Response::FORMAT_JSON; $data = Yii::$app->request->get(); @@ -76,7 +88,7 @@ class SessionController extends Controller { return $textures->getMinecraftResponse(); } - public function actionHasJoinedLegacy() { + public function actionHasJoinedLegacy(): string { Yii::$app->response->format = Response::FORMAT_RAW; $data = Yii::$app->request->get(); @@ -95,7 +107,14 @@ class SessionController extends Controller { return 'YES'; } - public function actionProfile($uuid) { + /** + * @param string $uuid + * + * @return array + * @throws ForbiddenOperationException + * @throws IllegalArgumentException + */ + public function actionProfile(string $uuid): array { try { $uuid = Uuid::fromString($uuid)->toString(); } catch (\InvalidArgumentException $e) { diff --git a/api/modules/session/models/HasJoinedForm.php b/api/modules/session/models/HasJoinedForm.php index da5a2ee..d6a547b 100644 --- a/api/modules/session/models/HasJoinedForm.php +++ b/api/modules/session/models/HasJoinedForm.php @@ -1,4 +1,6 @@ protocol = $protocol; parent::__construct($config); + $this->protocol = $protocol; } + /** + * @return Account + * @throws ForbiddenOperationException + * @throws IllegalArgumentException + */ public function hasJoined(): Account { Yii::$app->statsd->inc('sessionserver.hasJoined.attempt'); if (!$this->protocol->validate()) { @@ -38,10 +48,9 @@ class HasJoinedForm extends Model { } $joinModel->delete(); + /** @var Account $account */ $account = $joinModel->getAccount(); - if ($account === null) { - throw new ErrorException('Account must exists'); - } + Assert::notNull($account); Session::info("User with username = '{$username}' successfully verified by server with server_id = '{$serverId}'."); Yii::$app->statsd->inc('sessionserver.hasJoined.success'); diff --git a/api/modules/session/models/JoinForm.php b/api/modules/session/models/JoinForm.php index bc4e2ad..29764ca 100644 --- a/api/modules/session/models/JoinForm.php +++ b/api/modules/session/models/JoinForm.php @@ -1,4 +1,6 @@ protocol = $protocol; $this->accessToken = $protocol->getAccessToken(); $this->selectedProfile = $protocol->getSelectedProfile(); $this->serverId = $protocol->getServerId(); - - parent::__construct($config); } - public function rules() { + public function rules(): array { return [ [['accessToken', 'serverId'], RequiredValidator::class], [['accessToken', 'selectedProfile'], 'validateUuid'], @@ -51,7 +52,12 @@ class JoinForm extends Model { ]; } - public function join() { + /** + * @return bool + * @throws IllegalArgumentException + * @throws ForbiddenOperationException + */ + public function join(): bool { $serverId = $this->serverId; $accessToken = $this->accessToken; Session::info("User with access_token = '{$accessToken}' trying join to server with server_id = '{$serverId}'."); @@ -62,9 +68,7 @@ class JoinForm extends Model { $account = $this->getAccount(); $sessionModel = new SessionModel($account->username, $serverId); - if (!$sessionModel->save()) { - throw new ErrorException('Cannot save join session model'); - } + Assert::true($sessionModel->save()); Session::info("User with access_token = '{$accessToken}' and nickname = '{$account->username}' successfully joined to server_id = '{$serverId}'."); Yii::$app->statsd->inc('sessionserver.join.success'); @@ -72,7 +76,14 @@ class JoinForm extends Model { return true; } - public function validate($attributeNames = null, $clearErrors = true) { + /** + * @param string $attributeNames + * @param bool $clearErrors + * + * @return bool + * @throws IllegalArgumentException + */ + public function validate($attributeNames = null, $clearErrors = true): bool { if (!$this->protocol->validate()) { throw new IllegalArgumentException(); } @@ -80,7 +91,12 @@ class JoinForm extends Model { return parent::validate($attributeNames, $clearErrors); } - public function validateUuid($attribute) { + /** + * @param string $attribute + * + * @throws IllegalArgumentException + */ + public function validateUuid(string $attribute): void { if ($this->hasErrors($attribute)) { return; } @@ -91,18 +107,19 @@ class JoinForm extends Model { } /** - * @throws \api\modules\session\exceptions\SessionServerException + * @throws \api\modules\session\exceptions\ForbiddenOperationException */ - public function validateAccessToken() { + public function validateAccessToken(): void { $accessToken = $this->accessToken; /** @var MinecraftAccessKey|null $accessModel */ - $accessModel = MinecraftAccessKey::findOne($accessToken); + $accessModel = MinecraftAccessKey::findOne(['access_token' => $accessToken]); if ($accessModel !== null) { Yii::$app->statsd->inc('sessionserver.authentication.legacy_minecraft_protocol'); /** @var MinecraftAccessKey|\api\components\OAuth2\Entities\AccessTokenEntity $accessModel */ if ($accessModel->isExpired()) { Session::error("User with access_token = '{$accessToken}' failed join by expired access_token."); Yii::$app->statsd->inc('sessionserver.authentication.legacy_minecraft_protocol_token_expired'); + throw new ForbiddenOperationException('Expired access_token.'); } @@ -117,6 +134,7 @@ class JoinForm extends Model { 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'); + throw new ForbiddenOperationException('Invalid access_token.'); } @@ -124,6 +142,7 @@ class JoinForm extends Model { 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'); + throw new ForbiddenOperationException('The token does not have required scope.'); } @@ -135,12 +154,14 @@ class JoinForm extends Model { if ($isUuid && $account->uuid !== $this->normalizeUUID($selectedProfile)) { 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'); + throw new ForbiddenOperationException('Wrong selected_profile.'); } if (!$isUuid && mb_strtolower($account->username) !== mb_strtolower($selectedProfile)) { 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'); + throw new ForbiddenOperationException('Invalid credentials'); }