public function __construct() { $serializedApiMethods = serialize($this->apiMethods); // Launch middleware $this->middleware('apiguard:' . $serializedApiMethods); // Attempt to get an authenticated user. $this->user = ApiGuardAuth::getUser(); $this->response = ApiResponseBuilder::build(); }
/** * Render an exception into an HTTP response. * * @param \Illuminate\Http\Request $request * @param \Exception $e * * @return \Illuminate\Http\Response */ public function render($request, Exception $e) { $response = ApiResponseBuilder::build(); if ($request->json()) { if ($e instanceof ModelNotFoundException || $e instanceof NotFoundHttpException) { return $response->errorNotFound(); } } return parent::render($request, $e); }
/** * Handle an incoming request. * * @param Request $request * @param Closure $next * @param null $serializedApiMethods * @return mixed * @throws Exception */ public function handle(Request $request, Closure $next, $serializedApiMethods = null) { // Unserialize parameters if (!is_null($serializedApiMethods)) { $this->apiMethods = unserialize($serializedApiMethods); } // Let's instantiate the response class first $response = ApiResponseBuilder::build(); // Let's get the method $method = $this->getRouteMethod(); if (empty($method)) { // There is no method return $response->errorMethodNotAllowed(); } // We should check if key authentication is enabled for this method $keyAuthentication = true; if (isset($this->apiMethods[$method]['keyAuthentication']) && $this->apiMethods[$method]['keyAuthentication'] === false) { $keyAuthentication = false; } if ($keyAuthentication === true) { $key = $request->header(config('apiguard.keyName', 'X-Authorization')); if (empty($key)) { // Try getting the key from elsewhere $key = $request->get(config('apiguard.keyName', 'X-Authorization')); } if (empty($key)) { // It's still empty! return $response->errorUnauthorized(); } $apiKeyModel = App::make(config('apiguard.models.apiKey', 'Chrisbjr\\ApiGuard\\Models\\ApiKey')); if (!$apiKeyModel instanceof ApiKeyRepository) { Log::error('[ApiGuard] Your ApiKey model should be an instance of ApiKeyRepository.'); throw new Exception("You ApiKey model should be an instance of ApiKeyRepository."); } $this->apiKey = $apiKeyModel->getByKey($key, config('apiguard.rememberApiKeyDuration', 0)); if (empty($this->apiKey)) { return $response->errorUnauthorized(); } // Authenticate the user of this API key ApiGuardAuth::authenticate($this->apiKey); // API key exists // Check level of API if (!empty($this->apiMethods[$method]['level'])) { if ($this->apiKey->level < $this->apiMethods[$method]['level']) { return $response->errorForbidden(); } } } $apiLog = App::make(config('apiguard.models.apiLog', 'Chrisbjr\\ApiGuard\\Models\\ApiLog')); if (!$apiLog instanceof ApiLogRepository) { Log::error('[ApiGuard] Your ApiLog model should be an instance of ApiLogRepository.'); throw new Exception("You ApiLog model should be an instance of ApiLogRepository."); } // Then check the limits of this method if (!empty($this->apiMethods[$method]['limits'])) { if (config('apiguard.logging', true) === false) { Log::warning("[ApiGuard] You specified a limit in the {$method} method but API logging needs to be enabled in the configuration for this to work."); } $limits = $this->apiMethods[$method]['limits']; // We get key level limits first if ($this->apiKey != null && !empty($limits['key'])) { $keyLimit = !empty($limits['key']['limit']) ? $limits['key']['limit'] : 0; if ($keyLimit == 0 || is_integer($keyLimit) == false) { Log::warning("[ApiGuard] You defined a key limit to the " . Route::currentRouteAction() . " route but you did not set a valid number for the limit variable."); } else { if (!$this->apiKey->ignore_limits) { // This means the apikey is not ignoring the limits $keyIncrement = !empty($limits['key']['increment']) ? $limits['key']['increment'] : config('apiguard.keyLimitIncrement', '1 hour'); $keyIncrementTime = strtotime('-' . $keyIncrement); if ($keyIncrementTime == false) { Log::warning("[ApiGuard] You have specified an invalid key increment time. This value can be any value accepted by PHP's strtotime() method"); } else { // Count the number of requests for this method using this api key $apiLogCount = $apiLog->countApiKeyRequests($this->apiKey->id, Route::currentRouteAction(), $request->getMethod(), $keyIncrementTime); if ($apiLogCount >= $keyLimit) { Log::warning("[ApiGuard] The API key ID#{$this->apiKey->id} has reached the limit of {$keyLimit} in the following route: " . Route::currentRouteAction()); return $response->setStatusCode(429)->withError('You have reached the limit for using this API.', 'GEN-TOO-MANY-REQUESTS'); } } } } } // Then the overall method limits if (!empty($limits['method'])) { $methodLimit = !empty($limits['method']['limit']) ? $limits['method']['limit'] : 0; if ($methodLimit == 0 || is_integer($methodLimit) == false) { Log::warning("[ApiGuard] You defined a method limit to the " . Route::currentRouteAction() . " route but you did not set a valid number for the limit variable."); } else { if ($this->apiKey != null && $this->apiKey->ignore_limits == true) { // then we skip this } else { $methodIncrement = !empty($limits['method']['increment']) ? $limits['method']['increment'] : config('apiguard.keyLimitIncrement', '1 hour'); $methodIncrementTime = strtotime('-' . $methodIncrement); if ($methodIncrementTime == false) { Log::warning("[ApiGuard] You have specified an invalid method increment time. This value can be any value accepted by PHP's strtotime() method"); } else { // Count the number of requests for this method $apiLogCount = $apiLog->countMethodRequests(Route::currentRouteAction(), $request->getMethod(), $methodIncrementTime); if ($apiLogCount >= $methodLimit) { Log::warning("[ApiGuard] The API has reached the method limit of {$methodLimit} in the following route: " . Route::currentRouteAction()); return $response->setStatusCode(429)->withError('The limit for using this API method has been reached', 'GEN-TOO-MANY-REQUESTS'); } } } } } } // End of cheking limits if (config('apiguard.logging', true)) { // Default to log requests from this action $logged = true; if (isset($this->apiMethods[$method]['logged']) && $this->apiMethods[$method]['logged'] === false) { $logged = false; } if ($logged) { // Log this API request $this->apiLog = App::make(config('apiguard.models.apiLog', 'Chrisbjr\\ApiGuard\\Models\\ApiLog')); if (isset($this->apiKey)) { $this->apiLog->api_key_id = $this->apiKey->id; } $this->apiLog->create(['api_key_id' => isset($this->apiKey) ? $this->apiKey->id : null, 'route' => Route::currentRouteAction(), 'method' => $request->getMethod(), 'params' => http_build_query(Input::all()), 'ip_address' => $request->getClientIp()]); } } return $next($request); }