Got ACL scope logic working again and began enforcing it. Breaking API change: assigning page title with $template->tpl_strings['PAGE_NAME'] will no longer work, use $template->assign_vars(). Workaround may be added later. Test for assign_vars method if compatibility needed. Added namespace processor API (non-breaking change). Several other things tweaked around as well.
--- a/includes/pageprocess.php Sat Jun 14 22:01:24 2008 -0400
+++ b/includes/pageprocess.php Sun Jun 15 00:59:37 2008 -0400
@@ -194,6 +194,15 @@
return false;
}
}
+
+ // Is there a custom function registered for handling this namespace?
+ if ( $proc = $paths->get_namespace_processor($this->namespace) )
+ {
+ // yes, just call that
+ // this is protected aggressively by the PathManager against overriding critical namespaces
+ return call_user_func($proc, $this);
+ }
+
$pathskey = $paths->nslist[ $this->namespace ] . $this->page_id;
$strict_no_headers = false;
if ( $this->namespace == 'Admin' && strstr($this->page_id, '/') )
--- a/includes/paths.php Sat Jun 14 22:01:24 2008 -0400
+++ b/includes/paths.php Sun Jun 15 00:59:37 2008 -0400
@@ -17,8 +17,18 @@
* @see http://enanocms.org/Help:API_Documentation
*/
-class pathManager {
- var $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $anonymous_page;
+class pathManager
+{
+ public $pages, $custom_page, $cpage, $page, $fullpage, $page_exists, $page_id, $namespace, $nslist, $admin_tree, $wiki_mode, $page_protected, $template_cache, $anonymous_page;
+
+ /**
+ * List of custom processing functions for namespaces. This is protected so trying to do anything with it will throw an error.
+ * @access private
+ * @var array
+ */
+
+ protected $namespace_processors;
+
function __construct()
{
global $db, $session, $paths, $template, $plugins; // Common objects
@@ -76,7 +86,7 @@
$session->register_acl_type('html_in_pages', AUTH_DISALLOW, 'perm_html_in_pages', Array('edit_page'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
$session->register_acl_type('php_in_pages', AUTH_DISALLOW, 'perm_php_in_pages', Array('edit_page', 'html_in_pages'), 'Article|User|Project|Template|File|Help|System|Category|Admin');
$session->register_acl_type('custom_user_title', AUTH_DISALLOW, 'perm_custom_user_title', Array(), 'User|Special');
- $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'perm_edit_acl', Array('read', 'post_comments', 'edit_comments', 'edit_page', 'view_source', 'mod_comments', 'history_view', 'history_rollback', 'history_rollback_extra', 'protect', 'rename', 'clear_logs', 'vote_delete', 'vote_reset', 'delete_page', 'set_wiki_mode', 'password_set', 'password_reset', 'mod_misc', 'edit_cat', 'even_when_protected', 'upload_files', 'upload_new_version', 'create_page', 'php_in_pages'));
+ $session->register_acl_type('edit_acl', AUTH_DISALLOW, 'perm_edit_acl', Array());
// DO NOT add new admin pages here! Use a plugin to call $paths->addAdminNode();
$this->addAdminNode('adm_cat_general', 'adm_page_general_config', 'GeneralConfig', scriptPath . '/images/icons/applets/generalconfig.png');
@@ -573,6 +583,56 @@
}
/**
+ * Registers a handler to manually process a namespace instead of the default PageProcessor behavior.
+ * The first and only parameter passed to the processing function will be the PageProcessor instance.
+ * @param string Namespace to process
+ * @param mixed Function address. Either a function name or an array of the form array(0 => mixed (string:class name or object), 1 => string:method)
+ */
+
+ function register_namespace_processor($namespace, $function)
+ {
+ if ( isset($this->namespace_processors[$namespace]) )
+ {
+ $processorname = ( is_string($this->namespace_processors[$namespace]) ) ?
+ $this->namespace_processors[$namespace] :
+ ( is_object($this->namespace_processors[$namespace][0]) ? get_class($this->namespace_processors[$namespace][0]) : $this->namespace_processors[$namespace][0] ) . '::' .
+ $this->namespace_processors[$namespace][1];
+
+ trigger_error("Namespace \"$namespace\" is already being processed by $processorname - replacing caller", E_USER_WARNING);
+ }
+ if ( !is_string($function) )
+ {
+ if ( !is_array($function) )
+ return false;
+ if ( count($function) != 2 )
+ return false;
+ if ( !is_string($function[0]) && !is_object($function[0]) )
+ return false;
+ if ( !is_string($function[1]) )
+ return false;
+ }
+
+ // security: don't allow Special or Admin namespaces to be overridden
+ if ( $namespace == 'Special' || $namespace == 'Admin' )
+ {
+ trigger_error("Security manager denied attempt to override processor for $namespace", E_USER_ERROR);
+ }
+
+ $this->namespace_processors[$namespace] = $function;
+ }
+
+ /**
+ * Returns a namespace processor if one exists, otherwise returns false.
+ * @param string Namespace
+ * @return mixed
+ */
+
+ function get_namespace_processor($namespace)
+ {
+ return ( isset($this->namespace_processors[$namespace]) ) ? $this->namespace_processors[$namespace] : false;
+ }
+
+ /**
* Fetches the page texts for searching
*/
@@ -829,7 +889,17 @@
$row = $db->fetchrow();
$db->free_result();
$search = new Searcher();
- $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
+
+ // if the page shouldn't be indexed, send a blank set of strings to the indexing engine
+ if ( $this->pages[$idstring]['visible'] == 0 )
+ {
+ $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>''));
+ }
+ else
+ {
+ $search->buildIndex(Array("ns={$namespace};pid={$page_id}"=>$row['page_text'] . ' ' . $this->pages[$idstring]['name']));
+ }
+
$new_index = $search->index;
if ( ENANO_DBLAYER == 'MYSQL' )
--- a/includes/sessions.php Sat Jun 14 22:01:24 2008 -0400
+++ b/includes/sessions.php Sun Jun 15 00:59:37 2008 -0400
@@ -2935,6 +2935,22 @@
}
/**
+ * Checks if the given ACL rule type applies to a namespace.
+ * @param string ACL rule type
+ * @param string Namespace
+ * @return bool
+ */
+
+ function check_acl_scope($acl_rule, $namespace)
+ {
+ if ( !isset($this->acl_scope[$acl_rule]) )
+ return false;
+ if ( $this->acl_scope[$acl_rule] === array('All') )
+ return true;
+ return ( in_array($namespace, $this->acl_scope[$acl_rule]) ) ? true : false;
+ }
+
+ /**
* Read all of our permissions from the database and process/apply them. This should be called after the page is determined.
* @access private
*/
@@ -3038,7 +3054,8 @@
}
else
{
- $this->acl_scope[$perm_type][] = $ns;
+ if ( $this->acl_scope[$perm_type] !== array('All') )
+ $this->acl_scope[$perm_type][] = $ns;
if ( isset($this->acl_types[$perm_type]) && !isset($this->perms[$perm_type]) )
{
$this->perms[$perm_type] = $this->acl_types[$perm_type];
@@ -3895,6 +3912,17 @@
unset($base['__resolve_table']);
}
+ foreach ( $acl_types as $perm_type => $_ )
+ {
+ if ( !$session->check_acl_scope($perm_type, $namespace) )
+ {
+ unset($acl_types[$perm_type]);
+ unset($acl_deps[$perm_type]);
+ unset($acl_descs[$perm_type]);
+ unset($base[$perm_type]);
+ }
+ }
+
$this->acl_deps = $acl_deps;
$this->acl_types = $acl_types;
$this->acl_descs = $acl_descs;
@@ -3991,6 +4019,9 @@
if ( $this->perms[$perm_type] == AUTH_DENY )
continue;
+ if ( !$session->check_acl_scope($perm_type, $this->namespace) )
+ continue;
+
$this->perm_resolve_table[$perm_type] = array(
'src' => $src,
'rule_id' => $row['rule_id']
@@ -4093,7 +4124,23 @@
else
{
// ACL type is undefined
- trigger_error('Unknown access type "' . $type . '"', E_USER_WARNING);
+ $caller = 'unknown';
+ if ( function_exists('debug_backtrace') )
+ {
+ if ( $bt = @debug_backtrace() )
+ {
+ foreach ( $bt as $trace )
+ {
+ $file = basename($trace['file']);
+ if ( $file != 'sessions.php' )
+ {
+ $caller = $file . ':' . $trace['line'];
+ break;
+ }
+ }
+ }
+ }
+ trigger_error('Unknown access type "' . $type . '", called from ' . $caller . '', E_USER_WARNING);
return false; // Be on the safe side and deny access
}
if ( !$no_deps )
--- a/includes/template.php Sat Jun 14 22:01:24 2008 -0400
+++ b/includes/template.php Sun Jun 15 00:59:37 2008 -0400
@@ -558,7 +558,7 @@
// Page toolbar
// Comments button
- if ( $perms->get_permissions('read') && getConfig('enable_comments')=='1' && $local_namespace != 'Special' && $local_namespace != 'Admin' && $local_cdata['comments_on'] == 1 )
+ if ( $perms->get_permissions('read') && getConfig('enable_comments')=='1' && $local_cdata['comments_on'] == 1 )
{
$e = $db->sql_query('SELECT approved FROM '.table_prefix.'comments WHERE page_id=\''.$local_page_id.'\' AND namespace=\''.$local_namespace.'\';');
@@ -610,7 +610,7 @@
$tb .= $button->run();
}
// Edit button
- if($perms->get_permissions('read') && ($local_namespace != 'Special' && $local_namespace != 'Admin' && $local_namespace != 'Anonymous') && ( $perms->get_permissions('edit_page') && ( ( $paths->page_protected && $perms->get_permissions('even_when_protected') ) || !$paths->page_protected ) ) )
+ if($perms->get_permissions('read') && $session->check_acl_scope('edit_page', $local_namespace) && ( $perms->get_permissions('edit_page') && ( ( $paths->page_protected && $perms->get_permissions('even_when_protected') ) || !$paths->page_protected ) ) )
{
$button->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_edit') . '" accesskey="e"',
@@ -621,7 +621,7 @@
$tb .= $button->run();
// View source button
}
- else if($perms->get_permissions('view_source') && ( !$perms->get_permissions('edit_page') || !$perms->get_permissions('even_when_protected') && $paths->page_protected ) && $local_namespace != 'Special' && $local_namespace != 'Admin' && $local_namespace != 'Anonymous')
+ else if ( $session->check_acl_scope('view_source', $local_namespace) && $perms->get_permissions('view_source') && ( !$perms->get_permissions('edit_page') || !$perms->get_permissions('even_when_protected') && $paths->page_protected ) && $local_namespace != 'Anonymous')
{
$button->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxEditor()); return false; }" title="' . $lang->get('onpage_tip_viewsource') . '" accesskey="e"',
@@ -632,7 +632,7 @@
$tb .= $button->run();
}
// History button
- if ( $perms->get_permissions('read') /* && $paths->wiki_mode */ && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' && $perms->get_permissions('history_view') )
+ if ( $perms->get_permissions('read') && $session->check_acl_scope('history_view', $local_namespace) && $local_page_exists && $perms->get_permissions('history_view') )
{
$button->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxHistory()); return false; }" title="' . $lang->get('onpage_tip_history') . '" accesskey="h"',
@@ -647,7 +647,7 @@
// Additional actions menu
// Rename button
- if ( $perms->get_permissions('read') && $local_page_exists && ( $perms->get_permissions('rename') && ( $paths->page_protected && $perms->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ if ( $perms->get_permissions('read') && $session->check_acl_scope('rename', $local_namespace) && $local_page_exists && ( $perms->get_permissions('rename') && ( $paths->page_protected && $perms->get_permissions('even_when_protected') || !$paths->page_protected ) ) )
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxRename()); return false; }" title="' . $lang->get('onpage_tip_rename') . '" accesskey="r"',
@@ -658,7 +658,7 @@
}
// Vote-to-delete button
- if ( $paths->wiki_mode && $perms->get_permissions('vote_delete') && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin')
+ if ( $paths->wiki_mode && $session->check_acl_scope('vote_delete', $local_namespace) && $perms->get_permissions('vote_delete') && $local_page_exists)
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxDelVote()); return false; }" title="' . $lang->get('onpage_tip_delvote') . '" accesskey="d"',
@@ -669,7 +669,7 @@
}
// Clear-votes button
- if ( $perms->get_permissions('read') && $paths->wiki_mode && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' && $perms->get_permissions('vote_reset') && $local_cdata['delvotes'] > 0)
+ if ( $perms->get_permissions('read') && $session->check_acl_scope('vote_reset', $local_namespace) && $paths->wiki_mode && $local_page_exists && $perms->get_permissions('vote_reset') && $local_cdata['delvotes'] > 0)
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxResetDelVotes()); return false; }" title="' . $lang->get('onpage_tip_resetvotes') . '" accesskey="y"',
@@ -680,7 +680,7 @@
}
// Printable page button
- if ( $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ if ( $local_page_exists )
{
$menubtn->assign_vars(array(
'FLAGS' => 'title="' . $lang->get('onpage_tip_printable') . '"',
@@ -691,7 +691,7 @@
}
// Protect button
- if($perms->get_permissions('read') && $paths->wiki_mode && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' && $perms->get_permissions('protect'))
+ if($perms->get_permissions('read') && $session->check_acl_scope('protect', $local_namespace) && $paths->wiki_mode && $local_page_exists && $perms->get_permissions('protect'))
{
$label = $this->makeParserText($tplvars['toolbar_label']);
@@ -745,7 +745,7 @@
}
// Wiki mode button
- if($perms->get_permissions('read') && $local_page_exists && $perms->get_permissions('set_wiki_mode') && $local_namespace != 'Special' && $local_namespace != 'Admin')
+ if($perms->get_permissions('read') && $session->check_acl_scope('set_wiki_mode', $local_namespace) && $local_page_exists && $perms->get_permissions('set_wiki_mode'))
{
// label at start
$label = $this->makeParserText($tplvars['toolbar_label']);
@@ -803,7 +803,7 @@
}
// Clear logs button
- if ( $perms->get_permissions('read') && $perms->get_permissions('clear_logs') && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ if ( $perms->get_permissions('read') && $session->check_acl_scope('clear_logs', $local_namespace) && $perms->get_permissions('clear_logs') )
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxClearLogs()); return false; }" title="' . $lang->get('onpage_tip_flushlogs') . '" accesskey="l"',
@@ -814,7 +814,7 @@
}
// Delete page button
- if ( $perms->get_permissions('read') && $perms->get_permissions('delete_page') && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ if ( $perms->get_permissions('read') && $session->check_acl_scope('delete_page', $local_namespace) && $perms->get_permissions('delete_page') && $local_page_exists )
{
$s = $lang->get('onpage_btn_deletepage');
if ( $local_cdata['delvotes'] == 1 )
@@ -844,7 +844,7 @@
}
// Password-protect button
- if(isset($local_cdata['password']))
+ if(isset($local_cdata['password']) && $session->check_acl_scope('password_set', $local_namespace) && $session->check_acl_scope('password_reset', $local_namespace))
{
if ( $local_cdata['password'] == '' )
{
@@ -855,11 +855,15 @@
$a = $perms->get_permissions('password_reset');
}
}
- else
+ else if ( $session->check_acl_scope('password_set', $local_namespace) )
{
$a = $perms->get_permissions('password_set');
}
- if ( $a && $perms->get_permissions('read') && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ else
+ {
+ $a = false;
+ }
+ if ( $a && $perms->get_permissions('read') && $local_page_exists )
{
// label at start
$label = $this->makeParserText($tplvars['toolbar_label']);
@@ -877,7 +881,7 @@
}
// Manage ACLs button
- if ( !$paths->anonymous_page && ( $perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') && $session->user_level >= USER_LEVEL_ADMIN ) ) )
+ if ( !$paths->anonymous_page && $session->check_acl_scope('edit_acl', $local_namespace) && ( $perms->get_permissions('edit_acl') || ( defined('ACL_ALWAYS_ALLOW_ADMIN_EDIT_ACL') && $session->user_level >= USER_LEVEL_ADMIN ) ) )
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { return ajaxOpenACLManager(); }" title="' . $lang->get('onpage_tip_aclmanager') . '" accesskey="m"',
@@ -888,7 +892,7 @@
}
// Administer page button
- if ( $session->user_level >= USER_LEVEL_ADMIN && $local_page_exists && $local_namespace != 'Special' && $local_namespace != 'Admin' )
+ if ( $session->user_level >= USER_LEVEL_ADMIN && $local_page_exists )
{
$menubtn->assign_vars(array(
'FLAGS' => 'onclick="if ( !KILL_SWITCH ) { void(ajaxAdminPage()); return false; }" title="' . $lang->get('onpage_tip_adminoptions') . '" accesskey="g"',
@@ -940,9 +944,9 @@
/* if($this->sidebar_extra == '') $this->tpl_bool['right_sidebar'] = false;
else */ $this->tpl_bool['right_sidebar'] = true;
- $this->tpl_bool['auth_rename'] = ( $local_page_exists && ( $perms->get_permissions('rename') && ( $paths->page_protected && $perms->get_permissions('even_when_protected') || !$paths->page_protected ) ) && $local_namespace != 'Special' && $local_namespace != 'Admin');
+ $this->tpl_bool['auth_rename'] = ( $local_page_exists && $session->check_acl_scope('rename', $local_namespace) && ( $perms->get_permissions('rename') && ( $paths->page_protected && $perms->get_permissions('even_when_protected') || !$paths->page_protected ) ));
- $this->tpl_bool['enable_uploads'] = ( getConfig('enable_uploads') == '1' && $perms->get_permissions('upload_files') ) ? true : false;
+ $this->tpl_bool['enable_uploads'] = ( getConfig('enable_uploads') == '1' && $session->get_permissions('upload_files') ) ? true : false;
$this->tpl_bool['stupid_mode'] = false;
@@ -1002,6 +1006,15 @@
$urlname_jssafe = sanitize_page_id($local_fullpage);
$physical_urlname_jssafe = sanitize_page_id($paths->fullpage);
+ if ( $session->check_acl_scope('even_when_protected', $local_namespace) )
+ {
+ $protected = $paths->page_protected && !$perms->get_permissions('even_when_protected');
+ }
+ else
+ {
+ $protected = false;
+ }
+
// Generate the dynamic javascript vars
$js_dynamic = ' <script type="text/javascript">// <![CDATA[
// This section defines some basic and very important variables that are used later in the static Javascript library.
@@ -1023,7 +1036,7 @@
var pref_disable_js_fx = ' . ( @$session->user_extra['disable_js_fx'] == 1 ? '1' : '0' ) . ';
var csrf_token = "' . $session->csrf_token . '";
var editNotice = \'' . ( (getConfig('wiki_edit_notice')=='1') ? str_replace("\n", "\\\n", RenderMan::render(getConfig('wiki_edit_notice_text'))) : '' ) . '\';
- var prot = ' . ( ($paths->page_protected && !$perms->get_permissions('even_when_protected')) ? 'true' : 'false' ) .'; // No, hacking this var won\'t work, it\'s re-checked on the server
+ var prot = ' . ( ($protected) ? 'true' : 'false' ) .'; // No, hacking this var won\'t work, it\'s re-checked on the server
var ENANO_SPECIAL_CREATEPAGE = \''. makeUrl($paths->nslist['Special'].'CreatePage') .'\';
var ENANO_CREATEPAGE_PARAMS = \'_do=&pagename='. $urlname_clean .'&namespace=' . $local_namespace . '\';
var ENANO_SPECIAL_CHANGESTYLE = \''. makeUrlNS('Special', 'ChangeStyle') .'\';