/**
  * Helper function to get the identifier from a property wrapper.
  *
  * @param \EntityMetadataWrapper $property_wrapper
  *   The property wrapper to get the ID from.
  *
  * @return string
  *   An identifier.
  */
 protected function propertyIdentifier(\EntityMetadataWrapper $property_wrapper)
 {
     // The property wrapper is a reference to another entity get the entity
     // ID.
     $file_array = $property_wrapper->value();
     $identifier = $file_array['fid'];
     $resource = $this->getResource();
     // TODO: Make sure we still want to support fullView.
     if (!$resource || !$identifier || isset($resource['fullView']) && $resource['fullView'] === FALSE) {
         return $identifier;
     }
     // If there is a resource that we are pointing to, we need to use the id
     // field that that particular resource has in its configuration. Trying to
     // load by the entity id in that scenario will lead to a 404.
     // We'll load the plugin to get the idField configuration.
     $instance_id = sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']);
     /* @var ResourceInterface $resource */
     $resource = restful()->getResourceManager()->getPluginCopy($instance_id, Request::create('', array(), RequestInterface::METHOD_GET));
     $plugin_definition = $resource->getPluginDefinition();
     if (empty($plugin_definition['dataProvider']['idField'])) {
         return $identifier;
     }
     try {
         $file_wrapper = entity_metadata_wrapper('file', $file_array['fid']);
         return $file_wrapper->{$plugin_definition['dataProvider']['idField']}->value();
     } catch (\EntityMetadataWrapperException $e) {
         return $identifier;
     }
 }
Beispiel #2
0
 /**
  * This method will parse the url and determine the controller, its method and its parameters if any.
  * Then we will include the controller file.
  * Then initialize the controller
  * then run its method
  * @return [nothing]
  */
 function handle_request()
 {
     $this->controller = $this->uri->url["controller"];
     $this->method = restful($this->uri->url["method"]);
     $this->params = $this->uri->url["params"];
     if ($this->debug) {
         dbug("The controller ", $this->controller);
         dbug("The method ", $this->method);
         dbug("The params ", $this->params);
     }
     // set file location
     $file = CONTROLLER_FOLDER . '/' . $this->controller . '.php';
     // check if the class file (controller) exist , else throw error
     if (!file_exists($file)) {
         header("Content-Type: application/json");
         echo json_encode(["status" => "fail", "response" => "The requested API does not exist."]);
         die(header("HTTP/1.0 404 Oops requested url does not exist or has moved "));
     }
     // include the class file (controller)
     require_once $file;
     $controller = $this->controller;
     // initialize the controller
     $this->app = new $controller($this->autoload);
     // if the method exist in this controller
     if (method_exists($this->app, $this->method)) {
         $this->app->{$this->method}($this->params);
     } else {
         header("Content-Type: application/json");
         echo json_encode(["status" => "fail", "response" => "The requested API does not exist."]);
         header("HTTP/1.0 404  Oops requested url does not exist  ");
     }
 }
 /**
  * {@inheritdoc}
  */
 public static function create(array $field, RequestInterface $request = NULL)
 {
     $request = $request ?: restful()->getRequest();
     $resource_field = new static($field, $request);
     $resource_field->addDefaults();
     return $resource_field;
 }
 /**
  * Returns a random relationship.
  *
  * This serves as an example of a use case for the generic relationship.
  *
  * @param DataInterpreterInterface $interpreter
  *   The data interpreter.
  *
  * @return mixed
  *   The embeddable result.
  */
 public static function randomRelationship(DataInterpreterInterface $interpreter)
 {
     /* @var \Drupal\restful\Plugin\resource\ResourceInterface $handler */
     $handler = restful()->getResourceManager()->getPlugin('db_query_test:1.0');
     // This simbolizes some complex logic that gets a rendered resource.
     $id = static::complexCalculation();
     return $handler->getDataProvider()->view($id);
 }
 /**
  * {@inheritdoc}
  */
 public static function init(RequestInterface $request, $resource_name, array $version, $resource_path = NULL)
 {
     /* @var ResourceInterface $resource */
     $instance_id = $resource_name . PluginBase::DERIVATIVE_SEPARATOR . $version[0] . '.' . $version[1];
     $resource = restful()->getResourceManager()->getPluginCopy($instance_id, Request::create('', array(), RequestInterface::METHOD_GET));
     $plugin_definition = $resource->getPluginDefinition();
     $resource->setPath($resource_path);
     return new static($request, $resource->getFieldDefinitions(), $resource->getAccount(), $resource->getPluginId(), $resource->getPath(), $plugin_definition['dataProvider'], static::getLanguage(), $resource);
 }
 /**
  * Login a user and return a JSON along with the authentication cookie.
  *
  * @return array
  *   Array with the public fields populated.
  */
 public function loginAndRespondWithCookie()
 {
     // Login the user.
     $account = $this->getAccount();
     $this->loginUser($account);
     $user_resource = restful()->getResourceManager()->getPlugin('users:1.0');
     // User resource may be disabled.
     $output = $user_resource ? $user_resource->view($account->uid) : array();
     if ($resource_field_collection = reset($output)) {
         /* @var $resource_field_collection \Drupal\restful\Plugin\resource\Field\ResourceFieldCollectionInterface */
         $resource_field_collection->set('X-CSRF-Token', ResourceField::create(array('public_name' => 'X-CSRF-Token', 'callback' => '\\Drupal\\restful\\Plugin\\resource\\LoginCookie__1_0::getCSRFTokenValue')));
     }
     return $output;
 }
 /**
  * Helper function to get a formatter and apply a method.
  *
  * @param string $method
  *   A valid method to call on the FormatterInterface object.
  * @param array $data
  *   The array of data to process.
  * @param string $formatter_name
  *   The name of the formatter for the current resource. Leave it NULL to use
  *   the Accept headers.
  *
  * @return string
  *   The processed output.
  */
 protected function processData($method, array $data, $formatter_name = NULL)
 {
     if ($resource = $this->resource) {
         $request = $resource->getRequest();
     } else {
         $request = restful()->getRequest();
     }
     $accept = $request->getHeaders()->get('accept')->getValueString();
     $formatter = $this->negotiateFormatter($accept, $formatter_name);
     $output = ResourceManager::executeCallback(array($formatter, $method), array($data, $formatter_name));
     // The content type header is modified after the massaging if there is
     // an error code. Therefore we need to set the content type header after
     // formatting the output.
     $content_type = $formatter->getContentTypeHeader();
     $response_headers = restful()->getResponse()->getHeaders();
     $response_headers->add(HttpHeader::create('Content-Type', $content_type));
     return $output;
 }
 /**
  * Returns the value for the current single field.
  *
  * This implementation will also add some metadata to the resource field
  * object about the entity it is referencing.
  *
  * @param \EntityMetadataWrapper $property_wrapper
  *   The property wrapper. Either \EntityDrupalWrapper or \EntityListWrapper.
  * @param \EntityDrupalWrapper $wrapper
  *   The entity wrapper.
  * @param object $account
  *   The user account.
  *
  * @return mixed
  *   A single value for the field.
  *
  * @throws \Drupal\restful\Exception\BadRequestException
  * @throws \Drupal\restful\Exception\ServerConfigurationException
  */
 protected function singleValue(\EntityMetadataWrapper $property_wrapper, \EntityDrupalWrapper $wrapper, $account)
 {
     if ($resource = $this->getResource()) {
         // TODO: The resource input data in the field definition has changed.
         // Now it does not need to be keyed by bundle since you don't even need
         // an entity to use the resource based field.
         $embedded_identifier = $this->propertyIdentifier($property_wrapper);
         // Allow embedding entities with ID 0, like the anon user.
         if (empty($embedded_identifier) && $embedded_identifier !== 0) {
             return NULL;
         }
         if (isset($resource['fullView']) && $resource['fullView'] === FALSE) {
             return $embedded_identifier;
         }
         // We support dot notation for the sparse fieldsets. That means that
         // clients can specify the fields to show based on the "fields" query
         // string parameter.
         $parsed_input = array('fields' => implode(',', $this->nestedDottedChildren('fields')), 'include' => implode(',', $this->nestedDottedChildren('include')), 'filter' => $this->nestedDottedChildren('filter'));
         $request = Request::create('', array_filter($parsed_input), RequestInterface::METHOD_GET);
         // Get a plugin (that can be altered with decorators.
         $embedded_resource = restful()->getResourceManager()->getPluginCopy(sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']));
         // Configure the plugin copy with the sub-request and sub-path.
         $embedded_resource->setPath($embedded_identifier);
         $embedded_resource->setRequest($request);
         $embedded_resource->setAccount($account);
         $metadata = $this->getMetadata($wrapper->getIdentifier());
         $metadata = $metadata ?: array();
         $metadata[] = $this->buildResourceMetadataItem($property_wrapper);
         $this->addMetadata($wrapper->getIdentifier(), $metadata);
         try {
             // Get the contents to embed in place of the reference ID.
             /* @var ResourceFieldCollection $embedded_entity */
             $embedded_entity = $embedded_resource->getDataProvider()->view($embedded_identifier);
         } catch (InaccessibleRecordException $e) {
             // If you don't have access to the embedded entity is like not having
             // access to the property.
             return NULL;
         } catch (UnprocessableEntityException $e) {
             // If you access a nonexistent embedded entity.
             return NULL;
         }
         // Test if the $embedded_entity meets the filter or not.
         if (empty($parsed_input['filter'])) {
             return $embedded_entity;
         }
         foreach ($parsed_input['filter'] as $filter) {
             // Filters only apply if the target is the current field.
             if (!empty($filter['target']) && $filter['target'] == $this->getPublicName() && !$embedded_entity->evalFilter($filter)) {
                 // This filter is not met.
                 return NULL;
             }
         }
         return $embedded_entity;
     }
     if ($this->getFormatter()) {
         // Get value from field formatter.
         $value = $this->formatterValue($property_wrapper, $wrapper);
     } else {
         // Single value.
         $value = $this->fieldValue($property_wrapper);
     }
     return $value;
 }
 /**
  * {@inheritdoc}
  */
 public static function create(array $field, RequestInterface $request = NULL)
 {
     $request = $request ?: restful()->getRequest();
     $resource_field = ResourceField::create($field, $request);
     $output = new static($field, $request);
     $output->decorate($resource_field);
     return $output;
 }
Beispiel #10
0
 protected function getHandler()
 {
     return restful()->getResourceManager()->getPlugin($this->style->getInstanceId());
 }
 /**
  * Parses the provided payload according to a content type.
  *
  * @param string $content_type
  *   The contents of the Content-Type header.
  *
  * @return array
  *   The parsed body.
  *
  * @throws \Drupal\restful\Exception\BadRequestException
  */
 protected static function parseBodyContentType($content_type)
 {
     if (!($input_string = file_get_contents('php://input'))) {
         return NULL;
     }
     if ($content_type == 'application/x-www-form-urlencoded') {
         $body = NULL;
         parse_str($input_string, $body);
         return $body;
     }
     // Use the Content Type header to negotiate a formatter to parse the body.
     $formatter = restful()->getFormatterManager()->negotiateFormatter($content_type);
     return $formatter->parseBody($input_string);
 }
 /**
  * {@inheritdoc}
  */
 public static function factory(array $fields = array(), RequestInterface $request = NULL)
 {
     // TODO: Explore the possibility to change factory methods by using FactoryInterface.
     return new static($fields, $request ?: restful()->getRequest());
 }
 /**
  * Given a field item that contains a cache placeholder render and cache it.
  *
  * @param array $field_item
  *   The output to render.
  *
  * @param string $includes_path
  *   The includes path encoded in dot notation.
  *
  * @return array
  *   The rendered embedded field item.
  *
  * @throws \Drupal\restful\Exception\BadRequestException
  * @throws \Drupal\restful\Exception\InternalServerErrorException
  */
 protected function populateCachePlaceholder(array $field_item, $includes_path)
 {
     if (empty($field_item['#cache_placeholder']) || empty($field_item['#resource_id']) || empty($field_item['#resource_plugin'])) {
         return $field_item;
     }
     $embedded_resource = restful()->getResourceManager()->getPluginCopy($field_item['#resource_plugin']);
     $input = $this->getRequest()->getParsedInput();
     $new_input = $input + array('include' => '', 'fields' => '');
     // If the field is not supposed to be included, then bail.
     $old_includes = array_filter(explode(',', $new_input['include']));
     if (!in_array($includes_path, $old_includes)) {
         return $field_item;
     }
     $new_input['fields'] = implode(',', $this->unprefixInputOptions(explode(',', $new_input['fields']), $includes_path));
     $new_input['include'] = implode(',', $this->unprefixInputOptions($old_includes, $includes_path));
     // Create a new request from scratch copying most of the values but the
     // $query.
     $embedded_resource->setRequest(Request::create($this->getRequest()->getPath(), array_filter($new_input), $this->getRequest()->getMethod(), $this->getRequest()->getHeaders(), $this->getRequest()->isViaRouter(), $this->getRequest()->getCsrfToken(), $this->getRequest()->getCookies(), $this->getRequest()->getFiles(), $this->getRequest()->getServer(), $this->getRequest()->getParsedBody()));
     try {
         $data = $embedded_resource->getDataProvider()->view($field_item['#resource_id']);
     } catch (InaccessibleRecordException $e) {
         // Populate it with an empty element.
         $data = array();
     }
     return array_merge($field_item, $this->extractFieldValues($data, $field_item['#cache_placeholder']['parents'], $field_item['#cache_placeholder']['parent_hashes']));
 }
 /**
  * Returns only the allowed fields by filtering out the other ones.
  *
  * @param mixed $output
  *   The data structure to filter.
  * @param bool|string[] $allowed_fields
  *   FALSE to allow all fields. An array of allowed values otherwise.
  *
  * @return mixed
  *   The filtered output.
  */
 protected function limitFields($output, $allowed_fields = NULL)
 {
     if (!isset($allowed_fields)) {
         $request = ($resource = $this->getResource()) ? $resource->getRequest() : restful()->getRequest();
         $input = $request->getParsedInput();
         // Set the field limits to false if there are no limits.
         $allowed_fields = empty($input['fields']) ? FALSE : explode(',', $input['fields']);
     }
     if (!is_array($output)) {
         // $output is a simple value.
         return $output;
     }
     $result = array();
     if (ResourceFieldBase::isArrayNumeric($output)) {
         foreach ($output as $item) {
             $result[] = $this->limitFields($item, $allowed_fields);
         }
         return $result;
     }
     foreach ($output as $field_name => $field_contents) {
         if ($allowed_fields !== FALSE && !in_array($field_name, $allowed_fields)) {
             continue;
         }
         $result[$field_name] = $this->limitFields($field_contents, $this->unprefixInputOptions($allowed_fields, $field_name));
     }
     return $result;
 }
 /**
  * {@inheritdoc}
  */
 public function addDefaults()
 {
     // Almost all the defaults come are applied by the object's property
     // defaults.
     if (!($resource = $this->getResource())) {
         return;
     }
     // Expand array to be verbose.
     if (!is_array($resource)) {
         $resource = array('name' => $resource);
     }
     // Set default value.
     $resource += array('fullView' => TRUE);
     // Set the default value for the version of the referenced resource.
     if (!isset($resource['majorVersion']) || !isset($resource['minorVersion'])) {
         list($major_version, $minor_version) = restful()->getResourceManager()->getResourceLastVersion($resource['name']);
         $resource['majorVersion'] = $major_version;
         $resource['minorVersion'] = $minor_version;
     }
     $this->setResource($resource);
 }
 /**
  * {@inheritdoc}
  */
 protected function initDataInterpreter($identifier)
 {
     $resource_manager = restful()->getResourceManager();
     try {
         $plugin = $resource_manager->getPlugin($identifier);
     } catch (UnauthorizedException $e) {
         return NULL;
     } catch (PluginNotFoundException $e) {
         throw new NotFoundException('Invalid URL path.');
     }
     // If the plugin is not discoverable throw an access denied exception.
     $definition = $plugin->getPluginDefinition();
     if (empty($definition['discoverable'])) {
         throw new InaccessibleRecordException(sprintf('The plugin %s is not discoverable.', $plugin->getResourceName()));
     }
     return new DataInterpreterPlug($this->getAccount(), new PluginWrapper($plugin));
 }
 /**
  * Adds the Allowed-Origin headers.
  *
  * @param string $path
  *   The requested path.
  */
 protected function preflight($path)
 {
     $plugin_definition = $this->getPluginDefinition();
     $header_bag = restful()->getResponse()->getHeaders();
     // Populate the Accept header.
     $accepted_formats = array();
     $formatter_manager = restful()->getFormatterManager();
     if (empty($plugin_definition['formatter'])) {
         foreach ($formatter_manager->getPlugins() as $formatter) {
             /** @var $formatter \Drupal\restful\Plugin\formatter\FormatterInterface */
             $header_bag->append(HttpHeader::create('Accept', $formatter->getContentTypeHeader()));
         }
     } else {
         try {
             $accepted_format = $formatter_manager->getPlugin($plugin_definition['formatter'])->getContentTypeHeader();
             $header_bag->add(HttpHeader::create('Accept', $accepted_format));
         } catch (PluginNotFoundException $e) {
             throw new NotImplementedException($e->getMessage());
         }
     }
     $allowed_origin = empty($plugin_definition['allowOrigin']) ? variable_get('restful_allowed_origin', NULL) : $plugin_definition['allowOrigin'];
     // Always add the allow origin if configured.
     if ($allowed_origin) {
         $header_bag->add(HttpHeader::create('Access-Control-Allow-Origin', check_plain($allowed_origin)));
         // @see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials
         $accepts_credentials = $allowed_origin == '*' ? 'false' : 'true';
         $header_bag->add(HttpHeader::create('Access-Control-Allow-Credentials', $accepts_credentials));
     }
     // Make sure the Access-Control-Allow-Methods is populated.
     $allowed_methods = array();
     foreach ($this->getControllers() as $pattern => $controllers) {
         // Find the controllers for the provided path.
         if ($pattern == $path || $pattern && preg_match('/' . $pattern . '/', $path)) {
             foreach ($controllers as $method => $controller) {
                 if (is_array($controller)) {
                     // If there is a custom access method for this endpoint check it.
                     if (!empty($selected_controller['access callback']) && !ResourceManager::executeCallback(array($this, $selected_controller['access callback']), array($path))) {
                         // There is no access for this method.
                         continue;
                     }
                 }
                 $allowed_methods[] = $method;
             }
             $header_bag->add(HttpHeader::create('Access-Control-Allow-Methods', implode(',', $allowed_methods)));
             break;
         }
     }
 }
 /**
  * Get the (reference) field information for a single item.
  *
  * @param ResourceFieldInterface $resource_field
  *   The resource field.
  *
  * @throws \Drupal\restful\Exception\BadRequestException
  *
  * @return array
  *   An array containing the following keys:
  *   - 'name': Drupal's internal field name. Ex: field_article_related
  *   - 'type': Either a field or a property.
  *   - 'entity_type': The entity type this field points to. Not populated if
  *     the field is not a reference (for instance the destination field used
  *     in the where clause).
  *   - 'bundles': The allowed bundles for this field. Not populated if the
  *     field is not a reference (for instance the destination field used in
  *     the where clause).
  */
 protected function getFieldsFromPublicNameItem(ResourceFieldInterface $resource_field)
 {
     $property = $resource_field->getProperty();
     $resource = $resource_field->getResource();
     $item = array('name' => $property, 'type' => ResourceFieldEntity::propertyIsField($property) ? RelationalFilterInterface::TYPE_FIELD : RelationalFilterInterface::TYPE_PROPERTY, 'entity_type' => NULL, 'bundles' => array());
     $item['column'] = $item['type'] == RelationalFilterInterface::TYPE_FIELD ? $resource_field->getColumn() : NULL;
     $instance_id = sprintf('%s:%d.%d', $resource['name'], $resource['majorVersion'], $resource['minorVersion']);
     /* @var ResourceEntity $resource */
     $resource = restful()->getResourceManager()->getPluginCopy($instance_id, Request::create('', array(), RequestInterface::METHOD_GET));
     // Variables for the next iteration.
     $definitions = $resource->getFieldDefinitions();
     $item['entity_type'] = $resource->getEntityType();
     $item['bundles'] = $resource->getBundles();
     return array($item, $definitions);
 }
 /**
  * Checks if the current request has reached the rate limit.
  *
  * If the user has reached the limit this method will throw an exception. If
  * not, the hits counter will be updated for subsequent calls. Since the
  * request can match multiple events, the access is only granted if all events
  * are cleared.
  *
  * @param RequestInterface $request
  *   The request array.
  *
  * @throws FloodException if the rate limit has been reached for the
  * current request.
  */
 public function checkRateLimit(RequestInterface $request)
 {
     $now = new \DateTime();
     $now->setTimestamp(REQUEST_TIME);
     // Check all rate limits configured for this handler.
     foreach ($this->plugins as $instance_id => $plugin) {
         // If the limit is unlimited then skip everything.
         /* @var RateLimit $plugin */
         $limit = $plugin->getLimit($this->account);
         $period = $plugin->getPeriod();
         if ($limit == static::UNLIMITED_RATE_LIMIT) {
             // User has unlimited access to the resources.
             continue;
         }
         // If the current request matches the configured event then check if the
         // limit has been reached.
         if (!$plugin->isRequestedEvent($request)) {
             continue;
         }
         if (!($rate_limit_entity = $plugin->loadRateLimitEntity($this->account))) {
             // If there is no entity, then create one.
             // We don't need to save it since it will be saved upon hit.
             $rate_limit_entity = entity_create('rate_limit', array('timestamp' => REQUEST_TIME, 'expiration' => $now->add($period)->format('U'), 'hits' => 0, 'event' => $plugin->getPluginId(), 'identifier' => $plugin->generateIdentifier($this->account)));
         }
         // When the new rate limit period starts.
         $new_period = new \DateTime();
         $new_period->setTimestamp($rate_limit_entity->expiration);
         if ($rate_limit_entity->isExpired()) {
             // If the rate limit has expired renew the timestamps and assume 0
             // hits.
             $rate_limit_entity->timestamp = REQUEST_TIME;
             $rate_limit_entity->expiration = $now->add($period)->format('U');
             $rate_limit_entity->hits = 0;
             if ($limit == 0) {
                 $exception = new FloodException('Rate limit reached');
                 $exception->setHeader('Retry-After', $new_period->format(\DateTime::RFC822));
                 throw $exception;
             }
         } else {
             if ($rate_limit_entity->hits >= $limit) {
                 $exception = new FloodException('Rate limit reached');
                 $exception->setHeader('Retry-After', $new_period->format(\DateTime::RFC822));
                 throw $exception;
             }
         }
         // Save a new hit after generating the exception to mitigate DoS attacks.
         $rate_limit_entity->hit();
         // Add the limit headers to the response.
         $remaining = $limit == static::UNLIMITED_RATE_LIMIT ? 'unlimited' : $limit - ($rate_limit_entity->hits + 1);
         $response = restful()->getResponse();
         $headers = $response->getHeaders();
         $headers->append(HttpHeader::create('X-Rate-Limit-Limit', $limit));
         $headers->append(HttpHeader::create('X-Rate-Limit-Remaining', $remaining));
         $time_remaining = $rate_limit_entity->expiration - REQUEST_TIME;
         $headers->append(HttpHeader::create('X-Rate-Limit-Reset', $time_remaining));
     }
 }