Skip to content

Commit 77e3744

Browse files
committed
Change authentication method for API.
1 parent 63f377a commit 77e3744

File tree

9 files changed

+162
-219
lines changed

9 files changed

+162
-219
lines changed

app/Http/Controllers/API/AuthController.php

Lines changed: 0 additions & 124 deletions
This file was deleted.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Middleware;
4+
5+
use Pterodactyl\Models\APIKey;
6+
use Pterodactyl\Models\APIPermission;
7+
8+
use Illuminate\Http\Request;
9+
use Dingo\Api\Routing\Route;
10+
use Dingo\Api\Auth\Provider\Authorization;
11+
12+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 400
13+
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; // 401
14+
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403
15+
16+
class APISecretToken extends Authorization
17+
{
18+
19+
protected $algo = 'sha256';
20+
21+
protected $permissionAllowed = false;
22+
23+
public function __construct()
24+
{
25+
//
26+
}
27+
28+
public function getAuthorizationMethod()
29+
{
30+
return 'Authorization';
31+
}
32+
33+
public function authenticate(Request $request, Route $route)
34+
{
35+
if (!$request->bearerToken() || empty($request->bearerToken())) {
36+
throw new UnauthorizedHttpException('The authentication header was missing or malformed');
37+
}
38+
39+
list($public, $hashed) = explode('.', $request->bearerToken());
40+
41+
$key = APIKey::where('public', $public)->first();
42+
if (!$key) {
43+
throw new AccessDeniedHttpException('Invalid API Key.');
44+
}
45+
46+
// Check for Resource Permissions
47+
if (!empty($request->route()->getName())) {
48+
if(!is_null($key->allowed_ips)) {
49+
if (!in_array($request->ip(), json_decode($key->allowed_ips))) {
50+
throw new AccessDeniedHttpException('This IP address does not have permission to use this API key.');
51+
}
52+
}
53+
54+
foreach(APIPermission::where('key_id', $key->id)->get() as &$row) {
55+
if ($row->permission === '*' || $row->permission === $request->route()->getName()) {
56+
$this->permissionAllowed = true;
57+
continue;
58+
}
59+
}
60+
61+
if (!$this->permissionAllowed) {
62+
throw new AccessDeniedHttpException('You do not have permission to access this resource.');
63+
}
64+
}
65+
66+
if($this->_generateHMAC($request->fullUrl(), $request->getContent(), $key->secret) !== base64_decode($hashed)) {
67+
throw new BadRequestHttpException('The hashed body was not valid. Potential modification of contents in route.');
68+
}
69+
70+
return true;
71+
72+
}
73+
74+
protected function _generateHMAC($url, $body, $key)
75+
{
76+
$data = urldecode($url) . '.' . $body;
77+
return hash_hmac($this->algo, $data, $key, true);
78+
}
79+
80+
}

app/Http/Routes/APIRoutes.php

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,7 @@ class APIRoutes
1010

1111
public function map(Router $router) {
1212

13-
app('Dingo\Api\Auth\Auth')->extend('jwt', function ($app) {
14-
return new \Dingo\Api\Auth\Provider\JWT($app['Tymon\JWTAuth\JWTAuth']);
15-
});
16-
1713
$api = app('Dingo\Api\Routing\Router');
18-
19-
$api->version('v1', function ($api) {
20-
$api->post('auth/login', [
21-
'as' => 'api.auth.login',
22-
'uses' => 'Pterodactyl\Http\Controllers\API\AuthController@postLogin'
23-
]);
24-
25-
$api->post('auth/validate', [
26-
'middleware' => 'api.auth',
27-
'as' => 'api.auth.validate',
28-
'uses' => 'Pterodactyl\Http\Controllers\API\AuthController@postValidate'
29-
]);
30-
});
31-
3214
$api->version('v1', ['middleware' => 'api.auth'], function ($api) {
3315

3416
/**

app/Models/API.php

Lines changed: 0 additions & 63 deletions
This file was deleted.

app/Models/APIKey.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Pterodactyl\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class APIKey extends Model
8+
{
9+
10+
/**
11+
* The table associated with the model.
12+
*
13+
* @var string
14+
*/
15+
protected $table = 'api_keys';
16+
17+
}

app/Models/APIPermission.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Pterodactyl\Models;
44

5-
use Debugbar;
65
use Illuminate\Database\Eloquent\Model;
76

87
class APIPermission extends Model
@@ -15,16 +14,4 @@ class APIPermission extends Model
1514
*/
1615
protected $table = 'api_permissions';
1716

18-
/**
19-
* Checks if an API key has a specific permission.
20-
*
21-
* @param int $id
22-
* @param string $permission
23-
* @return boolean
24-
*/
25-
public static function check($id, $permission)
26-
{
27-
return self::where('key_id', $id)->where('permission', $permission)->exists();
28-
}
29-
3017
}

config/api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@
155155
*/
156156

157157
'auth' => [
158-
'jwt' => 'Dingo\Api\Auth\Provider\JWT'
158+
'custom' => 'Pterodactyl\Http\Middleware\APISecretToken'
159159
],
160160

161161
/*
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Schema\Blueprint;
4+
use Illuminate\Database\Migrations\Migration;
5+
6+
class CreateTableApiKeys extends Migration
7+
{
8+
/**
9+
* Run the migrations.
10+
*
11+
* @return void
12+
*/
13+
public function up()
14+
{
15+
Schema::create('api_keys', function (Blueprint $table) {
16+
$table->increments('id');
17+
$table->char('public', 16);
18+
$table->char('secret', 32);
19+
$table->json('allowed_ips')->nullable();
20+
$table->timestamps();
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*
27+
* @return void
28+
*/
29+
public function down()
30+
{
31+
Schema::drop('api_keys');
32+
}
33+
}

0 commit comments

Comments
 (0)