/** * Checks if current user has access to the current rest request. * * @param RequestBag $requestBag * * @return bool * @throws \Webiny\Component\Rest\RestException */ public static function hasAccess(RequestBag $requestBag) { // first we check if method requires a special access level if (!$requestBag->getApiConfig()->get('Security', false)) { return true; // no special access level required } // get the required role if (isset($requestBag->getMethodData()['role'])) { $role = $requestBag->getMethodData()['role']; } else { $role = $requestBag->getApiConfig()->get('Security.Role', 'ROLE_ANONYMOUS'); } // check if user has the access level required if ($requestBag->getClassData()['accessInterface']) { return $requestBag->getClassInstance()->hasAccess($role); } else { // get firewall name $firewallName = $requestBag->getApiConfig()->get('Security.Firewall', false); if (!$firewallName) { throw new RestException('When using Rest access rule, you must specify a Firewall in your configuration. Alternatively you can implement the AccessInterface and do the check on your own.'); } return self::security()->firewall($firewallName)->getUser()->hasRole($role); } }
/** * Computes the cache key, or gets it from the implemented interface from the api class. * * @return string Cache key. */ private function getCacheKey() { if ($this->requestBag->getClassData()['cacheKeyInterface']) { $cacheKey = $this->requestBag->getClassInstance()->getCacheKey(); } else { $url = $this->httpRequest()->getCurrentUrl(true); $cacheKey = 'path-' . $url->getPath(); $cacheKey .= 'query-' . $this->serialize($url->getQuery()); $cacheKey .= 'method-' . $this->httpRequest()->getRequestMethod(); $cacheKey .= 'post-' . $this->serialize($this->httpRequest()->getPost()->getAll()); $cacheKey .= 'payload-' . $this->serialize($this->httpRequest()->getPayload()->getAll()); $cacheKey .= 'version-' . $this->requestBag->getClassData()['version']; $cacheKey = md5($cacheKey); } return $cacheKey; }
public function testSetClassInstance() { $rb = new RequestBag(); $rb->setClassInstance('instance'); $this->assertSame('instance', $rb->getClassInstance()); }
/** * Processes the callback and returns an instance of CallbackResult. * * @return CallbackResult * @throws \Webiny\Component\Rest\RestException */ public function getCallbackResult() { $class = $this->requestBag->getClassData()['class']; $this->requestBag->setClassInstance(new $class()); // create CallbackResult instance $cr = new CallbackResult(); $env = 'production'; if ($this->requestBag->getApiConfig()->get('Environment', 'production') == 'development') { $cr->setEnvToDevelopment(); $env = 'development'; } // attach some metadata $cr->attachDebugHeader('Class', $class); $cr->attachDebugHeader('ClassVersion', $this->requestBag->getClassData()['version']); $cr->attachDebugHeader('Method', strtoupper($this->httpRequest()->getRequestMethod())); if (!$this->requestBag->getMethodData()) { // if no method matched the request $cr->setHeaderResponse(404); $cr->setErrorResponse('No service matched the request.'); return $cr; } // check rate limit try { $rateControl = RateControl::isWithinRateLimits($this->requestBag, $cr); if (!$rateControl) { $cr->setHeaderResponse(429); $cr->setErrorResponse('Rate control limit reached.'); return $cr; } } catch (\Exception $e) { throw new RestException('Rate control verification failed. ' . $e->getMessage()); } // verify access role try { $hasAccess = Security::hasAccess($this->requestBag); if (!$hasAccess) { $cr->setHeaderResponse(403); $cr->setErrorResponse('You don\'t have the required access level.'); $cr->attachDebugHeader('RequestedRole', $this->requestBag->getMethodData()['role']); return $cr; } } catch (\Exception $e) { throw new RestException('Access role verification failed. ' . $e->getMessage()); } // verify cache try { $cachedResult = Cache::getFromCache($this->requestBag); } catch (\Exception $e) { throw new RestException('Reading result from cache failed. ' . $e->getMessage()); } // finalize output if ($cachedResult) { $cr->setData($cachedResult); $cr->attachDebugHeader('Cache', 'HIT'); } else { try { $result = call_user_func_array([$this->requestBag->getClassInstance(), $this->requestBag->getMethodData()['method']], $this->requestBag->getMethodParameters()); // check if method has custom headers set $cr->setHeaderResponse($this->requestBag->getMethodData()['header']['status']['success']); $cr->attachDebugHeader('Cache', 'MISS'); // add result to output $cr->setData($result); // check if we need to attach the cache headers if ($this->requestBag->getMethodData()['header']['cache']['expires'] > 0) { $cr->setExpiresIn($this->requestBag->getMethodData()['header']['cache']['expires']); } // cache the result Cache::saveResult($this->requestBag, $result); } catch (RestErrorException $re) { // check if method has custom headers set $cr->setHeaderResponse($statusCode = $re->getResponseCode(), $this->requestBag->getMethodData()['header']['status']['errorMessage']); // check if a custom http response code is set $cr->setErrorResponse($re->getErrorMessage(), $re->getErrorDescription(), $re->getErrorCode()); $errors = $re->getErrors(); foreach ($errors as $e) { $cr->addErrorMessage($e); } if ($env == 'development') { $cr->addDebugMessage(['file' => $re->getFile(), 'line' => $re->getLine(), 'traces' => explode('#', $re->getTraceAsString())]); } } catch (\Exception $e) { // check if method has custom headers set $cr->setHeaderResponse($this->requestBag->getMethodData()['header']['status']['error'], $this->requestBag->getMethodData()['header']['status']['errorMessage']); $cr->setErrorResponse('There has been an error processing the request.'); if ($env == 'development') { $cr->addErrorMessage(['message' => $e->getMessage()]); $cr->addDebugMessage(['file' => $e->getFile(), 'line' => $e->getLine(), 'traces' => explode('#', $e->getTraceAsString())]); } } } return $cr; }