GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — develop ( 10ee77...db168e )
by Dane
07:24 queued 04:36
created

HMACAuthorization::public()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * Pterodactyl - Panel
4
 * Copyright (c) 2015 - 2017 Dane Everitt <[email protected]>.
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to deal
8
 * in the Software without restriction, including without limitation the rights
9
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
 * copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in all
14
 * copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
 * SOFTWARE.
23
 */
24
25
namespace Pterodactyl\Http\Middleware;
26
27
use Auth;
28
use Crypt;
29
use Config;
30
use Closure;
31
use Debugbar;
32
use IPTools\IP;
33
use IPTools\Range;
34
use Illuminate\Http\Request;
35
use Pterodactyl\Models\APIKey;
36
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 400
37
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403
38
39
class HMACAuthorization
40
{
41
    /**
42
     * The algorithm to use for handling HMAC encryption.
43
     *
44
     * @var string
45
     */
46
    const HMAC_ALGORITHM = 'sha256';
47
48
    /**
49
     * Stored values from the Authorization header.
50
     *
51
     * @var array
52
     */
53
    protected $token = [];
54
55
    /**
56
     * The eloquent model for the API key.
57
     *
58
     * @var \Pterodactyl\Models\APIKey
59
     */
60
    protected $key;
61
62
    /**
63
     * The illuminate request object.
64
     *
65
     * @var \Illuminate\Http\Request
66
     */
67
    private $request;
68
69
    /**
70
     * Construct class instance.
71
     *
72
     * @return void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
73
     */
74
    public function __construct()
75
    {
76
        Debugbar::disable();
77
        Config::set('session.driver', 'array');
78
    }
79
80
    /**
81
     * Handle an incoming request for the API.
82
     *
83
     * @param  \Illuminate\Http\Request  $request
84
     * @param  \Closure                  $next
85
     * @return mixed
86
     */
87
    public function handle(Request $request, Closure $next)
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
88
    {
89
        $this->request = $request;
90
91
        $this->checkBearer();
92
        $this->validateRequest();
93
        $this->validateIPAccess();
94
        $this->validateContents();
95
96
        Auth::loginUsingId($this->key()->user_id);
97
98
        return $next($request);
99
    }
100
101
    /**
102
     * Checks that the Bearer token is provided and in a valid format.
103
     *
104
     * @return void
105
     *
106
     * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
107
     */
108
    protected function checkBearer()
109
    {
110
        if (empty($this->request()->bearerToken())) {
111
            throw new BadRequestHttpException('Request was missing required Authorization header.');
112
        }
113
114
        $token = explode('.', $this->request()->bearerToken());
115
        if (count($token) !== 2) {
116
            throw new BadRequestHttpException('The Authorization header passed was not in a validate public/private key format.');
117
        }
118
119
        $this->token = [
120
            'public' => $token[0],
121
            'hash' => $token[1],
122
        ];
123
    }
124
125
    /**
126
     * Determine if the request contains a valid public API key
127
     * as well as permissions for the resource.
128
     *
129
     * @return void
130
     *
131
     * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
132
     */
133
    protected function validateRequest()
134
    {
135
        $this->key = APIKey::where('public', $this->public())->first();
136
        if (! $this->key) {
137
            throw new AccessDeniedHttpException('Unable to identify requester. Authorization token is invalid.');
138
        }
139
    }
140
141
    /**
142
     * Determine if the requesting IP address is allowed to use this API key.
143
     *
144
     * @return bool
145
     *
146
     * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
147
     */
148
    protected function validateIPAccess()
149
    {
150
        if (! is_null($this->key()->allowed_ips)) {
151
            foreach ($this->key()->allowed_ips as $ip) {
152
                if (Range::parse($ip)->contains(new IP($this->request()->ip()))) {
153
                    return true;
154
                }
155
            }
156
157
            throw new AccessDeniedHttpException('This IP address does not have permission to access the API using these credentials.');
158
        }
159
160
        return true;
161
    }
162
163
    /**
164
     * Determine if the HMAC sent in the request matches the one generated
165
     * on the panel side.
166
     *
167
     * @return void
168
     *
169
     * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
170
     */
171
    protected function validateContents()
172
    {
173
        if (base64_decode($this->hash()) !== $this->generateSignature()) {
174
            throw new BadRequestHttpException('The HMAC for the request was invalid.');
175
        }
176
    }
177
178
    /**
179
     * Generate a HMAC from the request and known API secret key.
180
     *
181
     * @return string
182
     */
183
    protected function generateSignature()
184
    {
185
        $content = urldecode($this->request()->fullUrl()) . $this->request()->getContent();
186
187
        return hash_hmac(self::HMAC_ALGORITHM, $content, Crypt::decrypt($this->key()->secret), true);
188
    }
189
190
    /**
191
     * Return the public key passed in the Authorization header.
192
     *
193
     * @return string
194
     */
195
    protected function public()
196
    {
197
        return $this->token['public'];
198
    }
199
200
    /**
201
     * Return the base64'd HMAC sent in the Authorization header.
202
     *
203
     * @return string
204
     */
205
    protected function hash()
206
    {
207
        return $this->token['hash'];
208
    }
209
210
    /**
211
     * Return the API Key model.
212
     *
213
     * @return \Pterodactyl\Models\APIKey
214
     */
215
    protected function key()
216
    {
217
        return $this->key;
218
    }
219
220
    /**
221
     * Return the Illuminate Request object.
222
     *
223
     * @return \Illuminate\Http\Request
224
     */
225
    private function request()
226
    {
227
        return $this->request;
228
    }
229
}
230