Commit 98421099 authored by japerry's avatar japerry

Improve access checks for panels routes

parent 3c7f39f7
......@@ -20,3 +20,13 @@ Upgrading from Panels-6.x-3.x to Panels-7.x-3.x
- display_renderer class is now in 'renderer', not 'handler'.
Upgrading task handlers from Panels 7.x-3.5 or older to Panels 7.x-3.6 and newer:
- You must specify a storage type for any panels display using your custom task handler.
For examples, see panels_update_7306.
- When creating whatever stores the panel, a storage id and storage type must be defined.
See panels_mini.module for examples inside panels_mini_save and panels_mini_panels_cache_get.
- A display access plugin must be defined.
See panels_mini/plugins/panels_storage/panels_mini.inc for an example plugin.
......@@ -478,6 +478,31 @@ function panels_get_renderer_pipelines($sort = TRUE) {
return $pipelines;
}
/**
* Fetch metadata on a specific panels_storage plugin.
*
* @param $storage
* Name of a panel_storage plugin.
*
* @return
* An array with information about the requested panels_storage plugin
*/
function panels_get_panels_storage_plugin($storage) {
ctools_include('plugins');
return ctools_get_plugins('panels', 'panels_storage', $storage);
}
/**
* Fetch metadata for all panels_storage plugins.
*
* @return
* An array of arrays with information about all available panels_storage plugins.
*/
function panels_get_panels_storage_plugins() {
ctools_include('plugins');
return ctools_get_plugins('panels', 'panels_storage');
}
/**
* Get a function from a plugin, if it exists.
*
......
......@@ -47,7 +47,25 @@ function panels_requirements_install() {
function panels_schema() {
// This should always point to our 'current' schema. This makes it relatively
// easy to keep a record of schema as we make changes to it.
return panels_schema_7();
return panels_schema_8();
}
function panels_schema_8() {
$schema = panels_schema_7();
// Add the storage type and id columns.
$schema['panels_display']['fields']['storage_type'] = array(
'type' => 'varchar',
'length' => 255,
'default' => '',
);
$schema['panels_display']['fields']['storage_id'] = array(
'type' => 'varchar',
'length' => 255,
'default' => '',
);
return $schema;
}
function panels_schema_7() {
......@@ -520,3 +538,53 @@ function panels_update_7304() {
}
}
}
/**
* Add the "storage_type" and "storage_id" columns to "panels_display".
*/
function panels_update_7305() {
$schema = panels_schema_8();
$new_fields = array(
'panels_display' => array('storage_type', 'storage_id'),
);
foreach ($new_fields as $table => $fields) {
foreach ($fields as $field_name) {
db_add_field($table, $field_name, $schema[$table]['fields'][$field_name]);
}
}
}
/**
* Set the storage type and id on existing page manager panels displays.
*/
function panels_update_7306() {
if (!db_table_exists('page_manager_handlers')) {
return t('Skipping update - page_manager is not installed.');
}
// Get all page_manager_handlers that have a panels context.
$result = db_query("SELECT pm.name, pm.conf FROM {page_manager_handlers} pm WHERE pm.handler = 'panel_context'");
$page_manager_panels = array();
foreach ($result as $row) {
$conf = unserialize($row->conf);
if (isset($conf['did'])) {
$page_manager_panels[$conf['did']] = $row->name;
}
}
if (!empty($page_manager_panels)) {
// Check panels displays that only have empty storage types
$result = db_query("SELECT pd.did FROM {panels_display} pd WHERE pd.did IN (:dids) AND storage_type = ''", array(':dids' => array_keys($page_manager_panels)));
foreach ($result as $row) {
db_update('panels_display')
->fields(array(
'storage_type' => 'page_manager',
'storage_id' => $page_manager_panels[$row->did],
))
->condition('did', $row->did)
->execute();
}
}
}
......@@ -409,6 +409,7 @@ function panels_ctools_plugin_type() {
'display_renderers' => array(
'classes' => array('renderer'),
),
'panels_storage' => array(),
);
}
......@@ -775,6 +776,55 @@ class panels_display {
}
return $output;
}
/**
* Determine if the given user can perform the requested operation.
*
* @param string $op
* An operation like: create, read, update, or delete.
* @param object $account
* (optional) The account to check access for.
*
* @return bool
* TRUE if access is granted; otherwise FALSE.
*/
function access($op, $account = NULL) {
global $user;
if (!$account) {
$account = $user;
}
// Even administrators need to go through the access system. However, to
// support legacy plugins, user 1 gets full access no matter what.
if ($account->uid == 1) {
return TRUE;
}
if (!in_array($op, array('create', 'read', 'update', 'delete', 'change layout'))) {
return FALSE;
}
if (empty($this->storage_type) || empty($this->storage_id)) {
return FALSE;
}
if ($this->storage_type == 'unknown') {
return FALSE;
}
$storage_plugin = panels_get_panels_storage_plugin($this->storage_type);
if (!$storage_plugin) {
return FALSE;
}
$access_callback = panels_plugin_get_function('panels_storage', $storage_plugin, 'access callback');
if (!$access_callback) {
return FALSE;
}
return $access_callback($this->storage_type, $this->storage_id, $op, $account);
}
}
/**
......@@ -1415,6 +1465,11 @@ function panels_ajax_router() {
ctools_include('cleanstring');
$renderer->clean_key = ctools_cleanstring($cache_key);
$op = $renderer->get_panels_storage_op_for_ajax($method);
if (!$cache->display->access($op)) {
return MENU_ACCESS_DENIED;
}
$output = call_user_func_array(array($renderer, $method), $args);
if (empty($output) && !empty($renderer->commands)) {
......@@ -1726,6 +1781,38 @@ function panels_page_wizard_panels_cache_set($key, $cache) {
page_manager_set_wizard_cache($wizard_cache);
}
/**
* Alter the page wizard basic page, when panels is selected, to inject page
* manager as the storage plugin for panels.
* @param $form
* @param $form_state
*/
function panels_form_page_manager_page_form_basic_alter(&$form, &$form_state) {
$form['#validate'][] = 'panels_page_manager_handler_add_validate';
}
/**
* Alter the variant add page, so when panels is selected, page manager is the
* storage plugin for panels.
* @param $form
* @param $form_state
*/
function panels_form_page_manager_handler_add_alter(&$form, &$form_state) {
$form['#validate'][] = 'panels_page_manager_handler_add_validate';
}
/**
* Perform the validation check to see if panel context is selected to use
* page manager as the storage plugin.
* @param $form
* @param $form_state
*/
function panels_page_manager_handler_add_validate($form, &$form_state) {
if($form_state['values']['handler'] == 'panel_context') {
$form_state['page']->storage_type = 'page_manager';
}
}
// --------------------------------------------------------------------------
// General utility functions
......
......@@ -261,6 +261,19 @@ class panels_renderer_ipe extends panels_renderer_editor {
}
}
function get_panels_storage_op_for_ajax($method) {
switch ($method) {
case 'ajax_unlock_ipe':
case 'ajax_save_form':
return 'update';
case 'ajax_change_layout':
case 'ajax_set_layout':
return 'change layout';
}
return parent::get_panels_storage_op_for_ajax($method);
}
/**
* AJAX callback to unlock the IPE.
*
......
......@@ -122,3 +122,61 @@ function panels_mini_uninstall() {
->execute();
}
}
/**
* Implements hook_update_dependencies().
*/
function panels_mini_update_dependencies() {
// Update 7301 requires panels storage support
$dependencies['panels_mini'][7301] = array(
'panels' => 7305,
);
return $dependencies;
}
/**
* Set the storage type and id on existing mini panels.
*/
function panels_mini_update_7301() {
if (!isset($sandbox['progress'])) {
// Initialize batch update information.
$sandbox['progress'] = (float)0;
$sandbox['current_did'] = -1;
$sandbox['max'] = db_query("SELECT COUNT(pd.did)
FROM {panels_display} pd
JOIN {panels_mini} pm ON pm.did = pd.did
WHERE pd.storage_type = ''")->fetchField();
}
// Set a limit of how many rows to process per batch.
$limit = 1000;
// Run the query
$result = db_query_range("SELECT pd.did, pm.name
FROM {panels_display} pd
JOIN {panels_mini} pm ON pm.did = pd.did
WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did']));
foreach ($result as $row) {
db_update('panels_display')
->fields(array(
'storage_type' => 'panels_mini',
'storage_id' => $row->name,
))
->condition('did', $row->did)
->execute();
// Update our progress information.
$sandbox['progress']++;
$sandbox['current_did'] = $row->did;
}
// Set the "finished" status, to tell batch engine whether this function
// needs to run again.
$sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
if ($sandbox['#finished']) {
return t('Added the storage type for panels_mini to relevant panels displays');
}
}
......@@ -365,6 +365,7 @@ function panels_mini_load_all($reset = FALSE) {
*/
function panels_mini_save(&$mini) {
if (!empty($mini->display)) {
$mini->display->storage_id = $mini->name;
$display = panels_save_display($mini->display);
$mini->did = $display->did;
}
......@@ -424,6 +425,9 @@ function panels_mini_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools' && ($plugin == 'content_types' || $plugin == 'export_ui')) {
return 'plugins/' . $plugin;
}
if ($module == 'panels' && $plugin == 'panels_storage') {
return 'plugins/' . $plugin;
}
}
/**
......@@ -462,6 +466,9 @@ function panels_mini_panels_cache_get($key) {
$cache->display = $item->display;
$cache->display->context = ctools_context_load_contexts($item);
$cache->display->cache_key = 'panels_mini:' . $key;
$cache->display->storage_type = 'panels_mini';
// Temporary storage id that's replaced in panels_mini_save().
$cache->display->storage_id = 'panels_mini';
$cache->content_types = panels_common_get_allowed_types('panels_mini', $cache->display->context);
$cache->display_title = TRUE;
......
<?php
/**
* @file
* Provides a panels_storage plugin for mini panels.
*/
// Plugin definition
$plugin = array(
'access callback' => 'panels_mini_panels_storage_access',
);
/**
* Access callback for panels storage.
*/
function panels_mini_panels_storage_access($storage_type, $storage_id, $op, $account) {
if ($op == 'create') {
return user_access('create mini panels', $account);
}
return user_access('administer mini panels', $account);
}
......@@ -56,6 +56,18 @@ function panels_node_uninstall() {
drupal_uninstall_schema('panels_node');
}
/**
* Implements hook_update_dependencies().
*/
function panels_node_update_dependencies() {
// Update 7301 requires panels storage support
$dependencies['panels_node'][7301] = array(
'panels' => 7305,
);
return $dependencies;
}
/**
* Implementation of hook_update to handle adding a pipeline
*/
......@@ -73,7 +85,7 @@ function panels_node_update_6001() {
/**
* Migrate legacy Drupal 6 permissions to Drupal 7.
*/
function panels_node_update_6002() {
function panels_node_update_7301() {
$permissions = array(
'create panel-nodes' => 'create panel content',
'edit any panel-nodes' => 'edit any panel content',
......@@ -107,3 +119,49 @@ function panels_node_update_6002() {
->execute();
}
}
/*
* Set the storage type and id on existing panels nodes.
*/
function panels_node_update_7302() {
if (!isset($sandbox['progress'])) {
// Initialize batch update information.
$sandbox['progress'] = (float)0;
$sandbox['current_did'] = -1;
$sandbox['max'] = db_query("SELECT COUNT(pd.did)
FROM {panels_display} pd
JOIN {panels_node} pn ON pn.did = pd.did
WHERE pd.storage_type = ''")->fetchField();
}
// Set a limit of how many rows to process per batch.
$limit = 1000;
// Run the query
$result = db_query_range("SELECT pd.did, pn.nid
FROM {panels_display} pd
JOIN {panels_node} pn ON pn.did = pd.did
WHERE pd.storage_type = '' AND pd.did > :current_did", 0, $limit, array(':current_did' => $sandbox['current_did']));
foreach ($result as $row) {
db_update('panels_display')
->fields(array(
'storage_type' => 'panels_node',
'storage_id' => $row->nid,
))
->condition('did', $row->did)
->execute();
// Update our progress information.
$sandbox['progress']++;
$sandbox['current_did'] = $row->did;
}
// Set the "finished" status, to tell batch engine whether this function
// needs to run again.
$sandbox['#finished'] = ($sandbox['progress'] >= $sandbox['max']) ? TRUE : ($sandbox['progress'] / $sandbox['max']);
if ($sandbox['#finished']) {
return t('Added the storage type for panels_node to relevant panels displays');
}
}
......@@ -24,6 +24,15 @@ function panels_node_permission() {
);
}
/**
* Implementation of hook_ctools_plugin_directory().
*/
function panels_node_ctools_plugin_directory($module, $plugin) {
if ($module == 'panels' && $plugin == 'panels_storage') {
return 'plugins/' . $plugin;
}
}
/**
* Implementation of hook_menu().
*/
......@@ -236,6 +245,8 @@ function panels_node_hook_insert(&$node) {
// Create a new display and record that.
$display = panels_new_display();
$display->layout = $node->panels_node['layout'];
$display->storage_type = 'panels_node';
$display->storage_id = $node->nid;
// Special handling for nodes being imported from an export.module data dump.
if (!empty($node->export_display)) {
......
<?php
/**
* @file
* Provides a panels_storage plugin for panels node.
*/
// Plugin definition
$plugin = array(
'access callback' => 'panels_node_panels_storage_access',
);
/**
* Access callback for panels storage.
*/
function panels_node_panels_storage_access($storage_type, $storage_id, $op, $account) {
if ($node = node_load($storage_id)) {
if ($op == 'read') {
$op = 'view';
}
return node_access($op, $node, $account);
}
return FALSE;
}
......@@ -514,6 +514,40 @@ class panels_renderer_editor extends panels_renderer_standard {
return $url;
}
/**
* Get the Panels storage oparation for a given renderer AJAX method.
*
* @param string $method
* The method name.
*
* @return string
* The Panels storage op.
*/
function get_panels_storage_op_for_ajax($method) {
switch ($method) {
case 'ajax_show':
case 'ajax_hide':
case 'ajax_select_content':
case 'ajax_add_pane':
case 'ajax_edit_pane':
case 'ajax_panel_title':
case 'ajax_cache_method':
case 'ajax_cache_settings':
case 'ajax_style_type':
case 'ajax_style_settings':
case 'ajax_pane_css':
case 'ajax_lock':
case 'ajax_access_settings':
case 'ajax_access_add_test':
case 'ajax_access_configure_test':
case 'ajax_layout':
case 'ajax_style':
return 'update';
}
return parent::get_panels_storage_op($method);
}
/**
* AJAX command to show a pane.
*/
......
......@@ -185,6 +185,19 @@ class panels_renderer_standard {
}
}
/**
* Get the Panels storage oparation for a given renderer AJAX method.
*
* @param string $method
* The method name.
*
* @return string
* The Panels storage op.
*/
function get_panels_storage_op_for_ajax($method) {
return 'read';
}
/**
* Prepare the attached display for rendering.
*
......
<?php
/**
* @file
* Provides a panels_storage plugin for page_manager.
*/
// Plugin definition
$plugin = array(
'access callback' => 'page_manager_panels_storage_access',
);
/**
* Access callback for panels storage.
*/
function page_manager_panels_storage_access($storage_type, $storage_id, $op, $account) {
// Only users with the 'use page manager' or administer page manager perms.
return user_access('use page manager', $account) || user_access('administer page manager', $account);
}
......@@ -586,6 +586,19 @@ function panels_panel_context_edit_choose($form, &$form_state) {
$form_state['display'] = &panels_panel_context_get_display($form_state['handler']);
// Grab the storage_type and storage_id and inject it into the display.
if (empty($form_state['display']->storage_type)) {
if (!isset($form_state[$form_state['task_id']]->storage_type)) {
watchdog('panels', "Unable to find the storage type for specified storage. Read 'Upgrading task handlers' in CHANGELOG.txt", array(), WATCHDOG_ERROR);
$form_state['display']->storage_type = 'unknown';
}
else {
$form_state['display']->storage_type = $form_state[$form_state['task_id']]->storage_type;
}
// When adding variants, we don't know the handler id yet. In that case,
// Mark it as new. We'll assign it later.
$form_state['display']->storage_id = !empty($form_state['handler_id']) ? $form_state['handler_id'] : 'new';
}
// Tell the Panels form not to display buttons.
$form_state['no buttons'] = TRUE;
......@@ -743,6 +756,10 @@ function panels_panel_context_edit_content_validate(&$form, &$form_state) {
}
function panels_panel_context_edit_content_submit(&$form, &$form_state) {
// Update the storage_id if this is a new variant before saving.
if ($form_state['display']->storage_id == 'new') {
$form_state['display']->storage_id = $form_state['handler_id'];
}
panels_edit_display_form_submit($form, $form_state);
$handler = &$form_state['handler'];
......
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