Commit a567be3e authored by Dries's avatar Dries

Issue #2054187 by juampy: Restrict authentication provider for REST resources.

parent 76aecc5d
......@@ -38,6 +38,15 @@ function file_help($path, $arg) {
}
}
/**
* Implements hook_modules_uninstalled().
*/
function file_modules_uninstalled($modules) {
foreach ($modules as $module) {
file_usage()->deleteByModule($module);
}
}
/**
* Implements hook_menu().
*/
......
......@@ -46,7 +46,7 @@ public function __construct(Connection $connection, $table = 'file_usage') {
}
/**
* Implements Drupal\file\FileUsage\FileUsageInterface::add().
* {@inheritdoc}
*/
public function add(File $file, $module, $type, $id, $count = 1) {
$this->connection->merge($this->tableName)
......@@ -64,7 +64,7 @@ public function add(File $file, $module, $type, $id, $count = 1) {
}
/**
* Implements Drupal\file\FileUsage\FileUsageInterface::delete().
* {@inheritdoc}
*/
public function delete(File $file, $module, $type = NULL, $id = NULL, $count = 1) {
// Delete rows that have a exact or less value to prevent empty rows.
......@@ -99,7 +99,7 @@ public function delete(File $file, $module, $type = NULL, $id = NULL, $count = 1
}
/**
* Implements Drupal\file\FileUsage\FileUsageInterface::listUsage().
* {@inheritdoc}
*/
public function listUsage(File $file) {
$result = $this->connection->select($this->tableName, 'f')
......@@ -113,4 +113,14 @@ public function listUsage(File $file) {
}
return $references;
}
/**
* {@inheritdoc}
*/
public function deleteByModule($module) {
$this->connection->delete($this->tableName)
->condition('module', $module)
->execute();
}
}
......@@ -72,4 +72,13 @@ public function delete(File $file, $module, $type = NULL, $id = NULL, $count = 1
*
*/
public function listUsage(File $file);
/**
* Removes all records for a specific module; useful for uninstalling modules.
*
* @param string $module
* The name of the module using files.
*/
public function deleteByModule($module);
}
......@@ -52,6 +52,44 @@ function testGetUsage() {
$this->assertEqual($usage['testing']['bar'][2], 2, 'Returned the correct count.');
}
/**
* Tests file_usage()->deleteByModule().
*/
function testDeleteByModule() {
$file = $this->createFile();
db_insert('file_usage')
->fields(array(
'fid' => $file->id(),
'module' => 'testing',
'type' => 'foo',
'id' => 1,
'count' => 1
))
->execute();
db_insert('file_usage')
->fields(array(
'fid' => $file->id(),
'module' => 'testing',
'type' => 'bar',
'id' => 2,
'count' => 2
))
->execute();
db_insert('file_usage')
->fields(array(
'fid' => $file->id(),
'module' => 'file',
'type' => 'bar',
'id' => 2,
'count' => 2
))
->execute();
file_usage()->deleteByModule('testing');
$this->assertIdentical(array('file' => array('bar' => array(2 => '2'))), file_usage()->listUsage($file), 'All recors for the "testing" module have been deleted.');
}
/**
* Tests file_usage()->add().
*/
......
......@@ -14,3 +14,13 @@ resources:
# GET:
# supported_formats:
# - json
#
# To enable only specific authentication methods for an operation, list them
# at supported_auth.
# For example, the following config only allows Basic HTTP authenticated
# requests for the POST method on the node entity.
# resources:
# entity:node:
# POST:
# supported_auth:
# - http_basic
......@@ -72,6 +72,11 @@ public function dynamicRoutes(RouteBuildEvent $event) {
$collection->add("rest.$name", $route);
continue;
}
// Check if there are authentication provider restrictions in the
// configuration and apply them to the route.
if (is_array($enabled_methods[$method]['supported_auth']) && !empty($enabled_methods[$method]['supported_auth'])) {
$route->setOption('_auth', $enabled_methods[$method]['supported_auth']);
}
// If there is no format requirement or if it matches the
// configuration also add the route.
$format_requirement = $route->getRequirement('_format');
......
<?php
/**
* @file
* Definition of Drupal\rest\test\AuthTest.
*/
namespace Drupal\rest\Tests;
use Drupal\rest\Tests\RESTTestBase;
/**
* Tests authenticated operations on test entities.
*/
class AuthTest extends RESTTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('hal', 'rest', 'entity_test');
/**
* {@inheritdoc}
*/
public static function getInfo() {
return array(
'name' => 'Resource authentication',
'description' => 'Tests authentication provider restrictions.',
'group' => 'REST',
);
}
/**
* Tests reading from an authenticated resource.
*/
public function testRead() {
$entity_type = 'entity_test';
// Enable a test resource through GET method and basic HTTP authentication.
$this->enableService('entity:' . $entity_type, 'GET', NULL, array('http_basic'));
// Create an entity programmatically.
$entity = $this->entityCreate($entity_type);
$entity->save();
// Try to read the resource as an anonymous user, which should not work.
$response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, $this->defaultMimeType);
$this->assertResponse('401', 'HTTP response code is 401 when the request is not authenticated and the user is anonymous.');
$this->assertText('A fatal error occurred: No authentication credentials provided.');
// Create a user account that has the required permissions to read
// resources via the REST API, but the request is authenticated
// with session cookies.
$permissions = $this->entityPermissions($entity_type, 'view');
$permissions[] = 'restful get entity:' . $entity_type;
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
// Try to read the resource with session cookie authentication, which is
// not enabled and should not work.
$response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, $this->defaultMimeType);
$this->assertResponse('403', 'HTTP response code is 403 when the request is authenticated but not authorized.');
$this->drupalLogout();
// Now read it with the Basic authentication which is enabled and should
// work.
$response = $this->basicAuthGet('entity/' . $entity_type . '/' . $entity->id(), $account->getUsername(), $account->pass_raw);
$this->assertResponse('200', 'HTTP response code is 200 for successfuly authorized requests.');
$this->curlClose();
}
/**
* Performs a HTTP request with Basic authentication.
*
* We do not use \Drupal\simpletest\WebTestBase::drupalGet because we need to
* set curl settings for basic authentication.
*
* @param string $path
* The request path.
* @param string $username
* The user name to authenticate with.
* @param string $password
* The password.
*
* @return string
* Curl output.
*/
protected function basicAuthGet($path, $username, $password) {
$out = $this->curlExec(
array(
CURLOPT_HTTPGET => TRUE,
CURLOPT_URL => url($path, array('absolute' => TRUE)),
CURLOPT_NOBODY => FALSE,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $username . ':' . $password,
)
);
$this->verbose('GET request to: ' . $path .
'<hr />' . $out);
return $out;
}
}
......@@ -192,8 +192,10 @@ protected function entityValues($entity_type) {
* The HTTP method to enable, e.g. GET, POST etc.
* @param string $format
* (Optional) The serialization format, e.g. hal_json.
* @param array $auth
* (Optional) The list of valid authentication methods.
*/
protected function enableService($resource_type, $method = 'GET', $format = NULL) {
protected function enableService($resource_type, $method = 'GET', $format = NULL, $auth = array()) {
// Enable REST API for this entity type.
$config = config('rest.settings');
$settings = array();
......@@ -205,6 +207,10 @@ protected function enableService($resource_type, $method = 'GET', $format = NULL
$settings[$resource_type][$method] = array();
}
}
if (is_array($auth) && !empty($auth)) {
$settings[$resource_type][$method]['supported_auth'] = $auth;
}
$config->set('resources', $settings);
$config->save();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment