2017-11-19 19:32:17 +00:00
< ? php
2018-02-25 21:30:56 +00:00
namespace Pterodactyl\Http\Middleware\Api ;
2017-11-19 19:32:17 +00:00
use Closure ;
2021-01-23 20:09:16 +00:00
use Carbon\CarbonImmutable ;
2017-11-19 19:32:17 +00:00
use Illuminate\Http\Request ;
2018-07-15 05:42:58 +00:00
use Pterodactyl\Models\User ;
2018-01-14 18:06:15 +00:00
use Pterodactyl\Models\ApiKey ;
2017-11-19 20:05:13 +00:00
use Illuminate\Auth\AuthManager ;
2021-11-17 04:02:18 +00:00
use Illuminate\Support\Facades\Session ;
2018-01-13 22:06:19 +00:00
use Illuminate\Contracts\Encryption\Encrypter ;
2017-11-19 20:05:13 +00:00
use Symfony\Component\HttpKernel\Exception\HttpException ;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException ;
2017-11-19 19:32:17 +00:00
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface ;
2017-11-19 20:05:13 +00:00
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException ;
2017-11-19 19:32:17 +00:00
class AuthenticateKey
{
2017-11-19 20:05:13 +00:00
/**
* @ var \Illuminate\Auth\AuthManager
*/
private $auth ;
2018-01-13 22:06:19 +00:00
/**
* @ var \Illuminate\Contracts\Encryption\Encrypter
*/
private $encrypter ;
2017-11-19 19:32:17 +00:00
/**
* @ var \Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface
*/
private $repository ;
/**
* AuthenticateKey constructor .
*/
2018-01-13 22:06:19 +00:00
public function __construct ( ApiKeyRepositoryInterface $repository , AuthManager $auth , Encrypter $encrypter )
{
2017-11-19 20:05:13 +00:00
$this -> auth = $auth ;
2018-01-13 22:06:19 +00:00
$this -> encrypter = $encrypter ;
2017-11-19 19:32:17 +00:00
$this -> repository = $repository ;
}
/**
2021-11-04 03:51:18 +00:00
* Handle an API request by verifying that the provided API key is in a valid
* format and exists in the database . If there is currently a user in the session
* do not even bother to look at the token ( they provided a cookie for this to
* be the case ) .
2017-11-19 19:32:17 +00:00
*
2017-11-19 20:05:13 +00:00
* @ return mixed
2017-11-19 20:34:55 +00:00
*
2018-01-14 19:30:55 +00:00
* @ throws \Pterodactyl\Exceptions\Model\DataValidationException
* @ throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
2017-11-19 19:32:17 +00:00
*/
2018-02-25 21:30:56 +00:00
public function handle ( Request $request , Closure $next , int $keyType )
2017-11-19 19:32:17 +00:00
{
2018-07-15 05:42:58 +00:00
if ( is_null ( $request -> bearerToken ()) && is_null ( $request -> user ())) {
2021-11-17 04:02:18 +00:00
throw new HttpException ( 401 , 'A bearer token or valid user session cookie must be provided to access this endpoint.' , null , [ 'WWW-Authenticate' => 'Bearer' ]);
2017-11-19 20:05:13 +00:00
}
2021-11-04 03:51:18 +00:00
// This is a request coming through using cookies, we have an authenticated user
// not using an API key. Make some fake API key models and continue on through
// the process.
if ( $request -> user () instanceof User ) {
2018-07-15 05:58:33 +00:00
$model = ( new ApiKey ()) -> forceFill ([
2018-07-15 05:42:58 +00:00
'user_id' => $request -> user () -> id ,
'key_type' => ApiKey :: TYPE_ACCOUNT ,
]);
2018-06-07 05:49:44 +00:00
} else {
2021-11-04 03:51:18 +00:00
$model = $this -> authenticateApiKey ( $request -> bearerToken (), $keyType );
2018-07-15 05:58:33 +00:00
$this -> auth -> guard () -> loginUsingId ( $model -> user_id );
2018-05-28 21:59:48 +00:00
}
2018-05-28 20:23:40 +00:00
2018-05-28 21:59:48 +00:00
$request -> attributes -> set ( 'api_key' , $model );
2018-05-28 20:23:40 +00:00
2018-05-28 21:59:48 +00:00
return $next ( $request );
}
2018-05-28 20:23:40 +00:00
2018-05-28 21:59:48 +00:00
/**
* Authenticate an API key .
*
* @ throws \Pterodactyl\Exceptions\Model\DataValidationException
* @ throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/
protected function authenticateApiKey ( string $key , int $keyType ) : ApiKey
{
$identifier = substr ( $key , 0 , ApiKey :: IDENTIFIER_LENGTH );
$token = substr ( $key , ApiKey :: IDENTIFIER_LENGTH );
2018-01-13 22:06:19 +00:00
2017-11-19 20:05:13 +00:00
try {
2018-01-14 19:30:55 +00:00
$model = $this -> repository -> findFirstWhere ([
[ 'identifier' , '=' , $identifier ],
2018-02-25 21:30:56 +00:00
[ 'key_type' , '=' , $keyType ],
2018-01-14 19:30:55 +00:00
]);
2017-11-19 20:05:13 +00:00
} catch ( RecordNotFoundException $exception ) {
2021-01-23 20:33:34 +00:00
throw new AccessDeniedHttpException ();
2017-11-19 20:05:13 +00:00
}
2021-01-23 20:33:34 +00:00
if ( ! hash_equals ( $this -> encrypter -> decrypt ( $model -> token ), $token )) {
throw new AccessDeniedHttpException ();
2018-01-13 22:06:19 +00:00
}
2021-01-23 20:09:16 +00:00
$this -> repository -> withoutFreshModel () -> update ( $model -> id , [ 'last_used_at' => CarbonImmutable :: now ()]);
2017-11-19 20:05:13 +00:00
2018-05-28 21:59:48 +00:00
return $model ;
2017-11-19 19:32:17 +00:00
}
}