1 file.module | _file_query_file_access_alter($query, $type) |
Helper for file entity access functions.
Parameters
$query: The query to add conditions to.
$type: Either 'file' or 'entity' depending on what sort of query it is. See file_query_file_access_alter() and file_query_entity_field_access_alter() for more.
Related topics
File
- core/
modules/ file/ file.module, line 2951 - Defines a "managed_file" Form API field and a "file" field for Field module.
Code
function _file_query_file_access_alter($query, $type) {
global $user;
// Read meta-data from query, if provided.
if (!$account = $query->getMetaData('account')) {
$account = $user;
}
// If $account can bypass file access, we don't need to alter the query.
if (user_access('bypass file access', $account)) {
return;
}
$tables = $query->getTables();
$base_table = $query->getMetaData('base_table');
// Do not use the base table for general entity queries unless this is
// querying the file_managed table directly.
if ($base_table && $type == 'entity' && $base_table != 'file_managed') {
$base_table = '';
}
// If no base table is specified explicitly, search for one.
if (!$base_table) {
$fallback = '';
foreach ($tables as $alias => $table_info) {
if (!($table_info instanceof SelectQueryInterface || $table_info['table'] instanceof SelectQueryInterface)) {
$table = $table_info['table'];
// If the file_managed table is in the query, it wins immediately.
if ($table == 'file_managed') {
$base_table = $table;
break;
}
// Check whether the table has a foreign key to file_managed.fid. If it
// does, do not run this check again as we found a base table and only
// file_managed can triumph that.
if (!$base_table) {
// The schema is cached.
$schema = backdrop_get_schema($table);
if (isset($schema['fields']['fid'])) {
if (isset($schema['foreign keys'])) {
foreach ($schema['foreign keys'] as $relation) {
if ($relation['table'] === 'file_managed' && $relation['columns'] === array('fid' => 'fid')) {
$base_table = $table;
}
}
}
else {
// At least it's a fid. A table with a field called fid is very
// very likely to be a file_managed.fid in a file access query.
$fallback = $table;
}
}
elseif (isset($schema['fields']['entity_id']) && isset($schema['fields']['entity_type']) && isset($schema['fields']['deleted']) && isset($schema['fields']['delta'])) {
// The table is a field data table, use it as fallback.
$base_table = $table;
}
}
}
}
// If there is nothing else, use the fallback.
if (!$base_table) {
if ($fallback) {
watchdog('security', 'Your file listing query is using @fallback as a base table in a query tagged for file access. This might not be secure and might not even work. Specify foreign keys in your schema to file_managed.fid ', array('@fallback' => $fallback), WATCHDOG_WARNING);
$base_table = $fallback;
}
else {
// Ignore this query as it was a general field query and no
// relationships were found to the file_managed table.
if ($type == 'entity') {
return;
}
throw new Exception(t('Query tagged for file access but there is no fid. Add foreign keys to file_managed.fid in schema to fix.'));
}
}
}
if ($type == 'entity') {
// The original query looked something like:
// @code
// SELECT fid FROM sometable s
// WHERE ($file_access_conditions)
// @endcode
//
// Our query will look like:
// @code
// SELECT entity_type, entity_id
// FROM field_data_something s
// WHERE (entity_type = 'file' AND $file_access_conditions) OR (entity_type <> 'file')
// @endcode
//
// So instead of directly adding to the query object, we need to collect
// all of the file access conditions in a separate db_and() object and
// then add it to the query at the end.
$file_conditions = db_and();
}
foreach ($tables as $falias => $tableinfo) {
$table = $tableinfo['table'];
if (!($table instanceof SelectQueryInterface) && $table == $base_table) {
$subquery = db_select('file_managed', 'fm_access')->fields('fm_access', array('fid'));
$subquery_conditions = db_or();
$wrappers = file_get_public_and_private_stream_wrapper_names();
if (!empty($wrappers['public'])) {
if (user_access('view files', $account)) {
foreach (array_keys($wrappers['public']) as $wrapper) {
$subquery_conditions->condition('fm_access.uri', $wrapper . '%', 'LIKE');
}
}
elseif (user_access('view own files', $account)) {
foreach (array_keys($wrappers['public']) as $wrapper) {
$subquery_conditions->condition(db_and()
->condition('fm_access.uri', $wrapper . '%', 'LIKE')
->condition('fm_access.uid', $account->uid)
);
}
}
}
if (!empty($wrappers['private'])) {
if (user_access('view private files', $account)) {
foreach (array_keys($wrappers['private']) as $wrapper) {
$subquery_conditions->condition('fm_access.uri', $wrapper . '%', 'LIKE');
}
}
elseif (user_access('view own private files', $account)) {
foreach (array_keys($wrappers['private']) as $wrapper) {
$subquery_conditions->condition(db_and()
->condition('fm_access.uri', $wrapper . '%', 'LIKE')
->condition('fm_access.uid', $account->uid)
);
}
}
}
// If there is no subquery conditions, the query is likely for file usage.
// Or user can only access public files.
// If there are subquery conditions then add them to the subquery.
if ($subquery_conditions->count() >= 1) {
$subquery->condition($subquery_conditions);
}
$field = 'fid';
// Now handle entities.
if ($type == 'entity') {
// Set a common alias for entities.
$base_alias = $falias;
$field = ($falias == 'file_managed' ? 'fid' : 'entity_id');
}
$subquery->where("$falias.$field = fm_access.fid");
// For an entity query, attach the subquery to entity conditions.
if ($type == 'entity') {
$file_conditions->exists($subquery);
}
// Otherwise attach it to the node query itself.
elseif ($table == 'file_managed') {
// Fix for https://drupal.org/node/2073085
$db_or = db_or();
$db_or->exists($subquery);
$db_or->isNull($falias . '.' . $field);
$query->condition($db_or);
}
else {
$query->exists($subquery);
}
}
}
if ($type == 'entity' && $file_conditions->count()) {
// All the file access conditions are only for field values belonging to
// files.
if ($base_alias !== 'file_managed') {
$file_conditions->condition("$base_alias.entity_type", 'file');
$or = db_or();
$or->condition($file_conditions);
// If the field value belongs to a non-file entity type then this function
// does not do anything with it.
$or->condition("$base_alias.entity_type", 'file', '<>');
// Add the compiled set of rules to the query.
$query->condition($or);
}
else {
$query->condition($file_conditions);
}
}
}