# HG changeset patch # User Dan # Date 1213505977 14400 # Node ID 66e14e61613ed08b478ee337fd382af39305d17f # Parent 4d0d5dae61e5bddb1e0f39dbdef0560eb3620231 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. diff -r 4d0d5dae61e5 -r 66e14e61613e includes/pageprocess.php --- 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, '/') ) diff -r 4d0d5dae61e5 -r 66e14e61613e includes/paths.php --- 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' ) diff -r 4d0d5dae61e5 -r 66e14e61613e includes/sessions.php --- 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 ) diff -r 4d0d5dae61e5 -r 66e14e61613e includes/template.php --- 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 = '