PocketMine-MP 5.15.1 git-ed158f8a1b0cfe334ac5f45febc0f633602014f2
LoginPacketHandler.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\network\mcpe\handler;
25
44use Ramsey\Uuid\Uuid;
45use function is_array;
46
55 public function __construct(
56 private Server $server,
57 private NetworkSession $session,
58 private \Closure $playerInfoConsumer,
59 private \Closure $authCallback
60 ){}
61
62 public function handleLogin(LoginPacket $packet) : bool{
63 $extraData = $this->fetchAuthData($packet->chainDataJwt);
64
65 if(!Player::isValidUserName($extraData->displayName)){
66 $this->session->disconnectWithError(KnownTranslationFactory::disconnectionScreen_invalidName());
67
68 return true;
69 }
70
71 $clientData = $this->parseClientData($packet->clientDataJwt);
72
73 try{
74 $skin = $this->session->getTypeConverter()->getSkinAdapter()->fromSkinData(ClientDataToSkinDataHelper::fromClientData($clientData));
75 }catch(\InvalidArgumentException | InvalidSkinException $e){
76 $this->session->disconnectWithError(
77 reason: "Invalid skin: " . $e->getMessage(),
78 disconnectScreenMessage: KnownTranslationFactory::disconnectionScreen_invalidSkin()
79 );
80
81 return true;
82 }
83
84 if(!Uuid::isValid($extraData->identity)){
85 throw new PacketHandlingException("Invalid login UUID");
86 }
87 $uuid = Uuid::fromString($extraData->identity);
88 $arrClientData = (array) $clientData;
89 $arrClientData["TitleID"] = $extraData->titleId;
90
91 if($extraData->XUID !== ""){
92 $playerInfo = new XboxLivePlayerInfo(
93 $extraData->XUID,
94 $extraData->displayName,
95 $uuid,
96 $skin,
97 $clientData->LanguageCode,
98 $arrClientData
99 );
100 }else{
101 $playerInfo = new PlayerInfo(
102 $extraData->displayName,
103 $uuid,
104 $skin,
105 $clientData->LanguageCode,
106 $arrClientData
107 );
108 }
109 ($this->playerInfoConsumer)($playerInfo);
110
111 $ev = new PlayerPreLoginEvent(
112 $playerInfo,
113 $this->session->getIp(),
114 $this->session->getPort(),
115 $this->server->requiresAuthentication()
116 );
117 if($this->server->getNetwork()->getValidConnectionCount() > $this->server->getMaxPlayers()){
118 $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_FULL, KnownTranslationFactory::disconnectionScreen_serverFull());
119 }
120 if(!$this->server->isWhitelisted($playerInfo->getUsername())){
121 $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_SERVER_WHITELISTED, KnownTranslationFactory::pocketmine_disconnect_whitelisted());
122 }
123
124 $banMessage = null;
125 if(($banEntry = $this->server->getNameBans()->getEntry($playerInfo->getUsername())) !== null){
126 $banReason = $banEntry->getReason();
127 $banMessage = $banReason === "" ? KnownTranslationFactory::pocketmine_disconnect_ban_noReason() : KnownTranslationFactory::pocketmine_disconnect_ban($banReason);
128 }elseif(($banEntry = $this->server->getIPBans()->getEntry($this->session->getIp())) !== null){
129 $banReason = $banEntry->getReason();
130 $banMessage = KnownTranslationFactory::pocketmine_disconnect_ban($banReason !== "" ? $banReason : KnownTranslationFactory::pocketmine_disconnect_ban_ip());
131 }
132 if($banMessage !== null){
133 $ev->setKickFlag(PlayerPreLoginEvent::KICK_FLAG_BANNED, $banMessage);
134 }
135
136 $ev->call();
137 if(!$ev->isAllowed()){
138 $this->session->disconnect($ev->getFinalDisconnectReason(), $ev->getFinalDisconnectScreenMessage());
139 return true;
140 }
141
142 $this->processLogin($packet, $ev->isAuthRequired());
143
144 return true;
145 }
146
150 protected function fetchAuthData(JwtChain $chain) : AuthenticationData{
152 $extraData = null;
153 foreach($chain->chain as $k => $jwt){
154 //validate every chain element
155 try{
156 [, $claims, ] = JwtUtils::parse($jwt);
157 }catch(JwtException $e){
158 throw PacketHandlingException::wrap($e);
159 }
160 if(isset($claims["extraData"])){
161 if($extraData !== null){
162 throw new PacketHandlingException("Found 'extraData' more than once in chainData");
163 }
164
165 if(!is_array($claims["extraData"])){
166 throw new PacketHandlingException("'extraData' key should be an array");
167 }
168 $mapper = new \JsonMapper();
169 $mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
170 $mapper->bExceptionOnMissingData = true;
171 $mapper->bExceptionOnUndefinedProperty = true;
172 $mapper->bStrictObjectTypeChecking = true;
173 try{
175 $extraData = $mapper->map($claims["extraData"], new AuthenticationData());
176 }catch(\JsonMapper_Exception $e){
177 throw PacketHandlingException::wrap($e);
178 }
179 }
180 }
181 if($extraData === null){
182 throw new PacketHandlingException("'extraData' not found in chain data");
183 }
184 return $extraData;
185 }
186
190 protected function parseClientData(string $clientDataJwt) : ClientData{
191 try{
192 [, $clientDataClaims, ] = JwtUtils::parse($clientDataJwt);
193 }catch(JwtException $e){
194 throw PacketHandlingException::wrap($e);
195 }
196
197 $mapper = new \JsonMapper();
198 $mapper->bEnforceMapType = false; //TODO: we don't really need this as an array, but right now we don't have enough models
199 $mapper->bExceptionOnMissingData = true;
200 $mapper->bExceptionOnUndefinedProperty = true;
201 $mapper->bStrictObjectTypeChecking = true;
202 try{
203 $clientData = $mapper->map($clientDataClaims, new ClientData());
204 }catch(\JsonMapper_Exception $e){
205 throw PacketHandlingException::wrap($e);
206 }
207 return $clientData;
208 }
209
216 protected function processLogin(LoginPacket $packet, bool $authRequired) : void{
217 $this->server->getAsyncPool()->submitTask(new ProcessLoginTask($packet->chainDataJwt->chain, $packet->clientDataJwt, $authRequired, $this->authCallback));
218 $this->session->setHandler(null); //drop packets received during login verification
219 }
220}
static parse(string $token)
Definition: JwtUtils.php:90
__construct(private Server $server, private NetworkSession $session, private \Closure $playerInfoConsumer, private \Closure $authCallback)
processLogin(LoginPacket $packet, bool $authRequired)
static isValidUserName(?string $name)
Definition: Player.php:199