1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-10 05:02:36 +01:00
invoiceninja/app/Http/Middleware/ApiCheck.php
2019-01-30 21:45:46 +11:00

114 lines
3.7 KiB
PHP
Executable File

<?php
namespace App\Http\Middleware;
use App\Models\AccountToken;
use Auth;
use Cache;
use Closure;
use Request;
use Response;
use Session;
use Utils;
/**
* Class ApiCheck.
*/
class ApiCheck
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
$loggingIn = $request->is('api/v1/login')
|| $request->is('api/v1/register')
|| $request->is('api/v1/oauth_login')
|| $request->is('api/v1/ios_subscription_status');
$headers = Utils::getApiHeaders();
$hasApiSecret = false;
if ($secret = env(API_SECRET)) {
$requestSecret = Request::header('X-Ninja-Secret') ?: ($request->api_secret ?: '');
$hasApiSecret = hash_equals($requestSecret, $secret);
} elseif (Utils::isSelfHost()) {
$hasApiSecret = true;
}
if ($loggingIn) {
// check API secret
if (! $hasApiSecret) {
sleep(ERROR_DELAY);
$error['error'] = ['message' => 'Invalid value for API_SECRET'];
return Response::json($error, 403, $headers);
}
} else {
// check for a valid token
$token = AccountToken::where('token', '=', Request::header('X-Ninja-Token'))->first(['id', 'user_id']);
// check if user is archived
if ($token && $token->user) {
Auth::onceUsingId($token->user_id);
session(['token_id' => $token->id]);
} elseif ($hasApiSecret && $request->is('api/v1/ping')) {
// do nothing: allow ping with api_secret or account token
} else {
sleep(ERROR_DELAY);
$error['error'] = ['message' => 'Invalid token'];
return Response::json($error, 403, $headers);
}
}
if (! Utils::isNinja() && ! $loggingIn) {
return $next($request);
}
$isMobileApp = strpos(array_get($_SERVER, 'HTTP_USER_AGENT'), '(dart:io)') !== false;
if (! Utils::hasFeature(FEATURE_API) && ! $hasApiSecret && ! $isMobileApp) {
$error['error'] = ['message' => 'API requires pro plan'];
return Response::json($error, 403, $headers);
} else {
$key = Auth::check() ? Auth::user()->account->id : $request->getClientIp();
// http://stackoverflow.com/questions/1375501/how-do-i-throttle-my-sites-api-users
$hour = 60 * 60;
$hour_limit = 1000;
$hour_throttle = Cache::get("hour_throttle:{$key}", null);
$last_api_request = Cache::get("last_api_request:{$key}", 0);
$last_api_diff = time() - $last_api_request;
if (is_null($hour_throttle)) {
$new_hour_throttle = 0;
} else {
$new_hour_throttle = $hour_throttle - $last_api_diff;
$new_hour_throttle = $new_hour_throttle < 0 ? 0 : $new_hour_throttle;
$new_hour_throttle += $hour / $hour_limit;
$hour_hits_remaining = floor(($hour - $new_hour_throttle) * $hour_limit / $hour);
$hour_hits_remaining = $hour_hits_remaining >= 0 ? $hour_hits_remaining : 0;
}
if ($new_hour_throttle > $hour) {
$wait = ceil($new_hour_throttle - $hour);
sleep(1);
return Response::json("Please wait {$wait} second(s)", 403, $headers);
}
Cache::put("hour_throttle:{$key}", $new_hour_throttle, 60);
Cache::put("last_api_request:{$key}", time(), 60);
}
return $next($request);
}
}