# HG changeset patch # User Dan # Date 1204430525 18000 # Node ID 7906fb190fc17c05ce04112d8044161ee8d68826 # Parent a044ad8346917351cf284326a87bf206904e8b53 Implemented all security features on theme disabling and ACLs; added clean_key mode to login API to clean unused encryption keys diff -r a044ad834691 -r 7906fb190fc1 includes/clientside/static/login.js --- a/includes/clientside/static/login.js Sat Mar 01 19:01:07 2008 -0500 +++ b/includes/clientside/static/login.js Sat Mar 01 23:02:05 2008 -0500 @@ -110,6 +110,12 @@ d.parentNode.removeChild(d); }, to); } + // Ask the server to clean our key + ajaxLoginPerformRequest({ + mode: 'clean_key', + key_aes: logindata.key_aes, + key_dh: logindata.key_dh + }); }; logindata.mb_object.onbeforeclick['OK'] = function() @@ -325,12 +331,12 @@ new messagebox(MB_ICONSTOP | MB_OK, 'FIXME L10N: There was an error in the login process', 'The following error code came from the server:
' + response.error); return false; } - // Rid ourselves of any loading windows - ajaxLoginSetStatus(AJAX_STATUS_DESTROY); // Main mode switch switch ( response.mode ) { case 'build_box': + // Rid ourselves of any loading windows + ajaxLoginSetStatus(AJAX_STATUS_DESTROY); // The server wants us to build the login form, all the information is there ajaxLoginBuildForm(response); break; @@ -339,6 +345,8 @@ logindata.successfunc(response.key); break; case 'login_failure': + // Rid ourselves of any loading windows + ajaxLoginSetStatus(AJAX_STATUS_DESTROY); document.getElementById('messageBox').style.backgroundColor = '#C0C0C0'; var mb_parent = document.getElementById('messageBox').parentNode; new Spry.Effect.Shake(mb_parent, {duration: 1500}).start(); @@ -349,6 +357,8 @@ ajaxLoginShowFriendlyError(response); }, 2500); break; + case 'noop': + break; } } diff -r a044ad834691 -r 7906fb190fc1 includes/sessions.php --- a/includes/sessions.php Sat Mar 01 19:01:07 2008 -0500 +++ b/includes/sessions.php Sat Mar 01 23:02:05 2008 -0500 @@ -551,14 +551,22 @@ die('No group info'); } } + + // make sure we aren't banned $this->check_banlist(); + // Printable page view? Probably the wrong place to control + // it but $template is pretty dumb, it will just about always + // do what you ask it to do, which isn't always what we want if ( isset ( $_GET['printable'] ) ) { $this->theme = 'printable'; $this->style = 'default'; } + // setup theme ACLs + $template->process_theme_acls(); + profiler_log('Sessions started'); } @@ -3209,6 +3217,23 @@ } break; + case 'clean_key': + // Clean out a key, since it won't be used. + if ( !empty($req['key_aes']) ) + { + $this->fetch_public_key($req['key_aes']); + } + if ( !empty($req['key_dh']) ) + { + $pk = $db->escape($req['key_dh']); + $q = $db->sql_query('DELETE FROM ' . table_prefix . "diffiehellman WHERE public_key = '$pk';"); + if ( !$q ) + $db->die_json(); + } + return array( + 'mode' => 'noop' + ); + break; } } diff -r a044ad834691 -r 7906fb190fc1 includes/template.php --- a/includes/template.php Sat Mar 01 19:01:07 2008 -0500 +++ b/includes/template.php Sat Mar 01 23:02:05 2008 -0500 @@ -16,6 +16,14 @@ var $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $namespace_string, $style_list, $theme_loaded; /** + * The list of themes that are critical for Enano operation. This doesn't include oxygen which + * remains a user theme. By default this is admin and printable which have to be loaded on demand. + * @var array + */ + + var $system_themes = array('admin', 'printable'); + + /** * Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php. * @var bool * @access private @@ -47,40 +55,139 @@ $this->theme_list = Array(); $this->named_theme_list = Array(); - $e = $db->sql_query('SELECT theme_id,theme_name,enabled,default_style FROM '.table_prefix.'themes WHERE enabled=1 ORDER BY theme_order;'); - if(!$e) $db->_die('The list of themes could not be selected.'); - for($i=0;$i < $db->numrows(); $i++) + + $q = $db->sql_query('SELECT theme_id, theme_name, enabled, default_style, group_policy, group_list FROM ' . table_prefix . 'themes;'); + if ( !$q ) + $db->_die('template.php selecting theme list'); + + $i = 0; + while ( $row = $db->fetchrow() ) { - $this->theme_list[$i] = $db->fetchrow(); - $this->named_theme_list[$this->theme_list[$i]['theme_id']] = $this->theme_list[$i]; + $this->theme_list[$i] = $row; + $i++; } - $db->free_result(); - $this->default_theme = $this->theme_list[0]['theme_id']; - $dir = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'; - $list = Array(); - // Open a known directory, and proceed to read its contents - if (is_dir($dir)) { - if ($dh = opendir($dir)) { - while (($file = readdir($dh)) !== false) { - if(preg_match('#^(.*?)\.css$#i', $file) && $file != '_printable.css') { - $list[] = substr($file, 0, strlen($file)-4); - } + // List out all CSS files for this theme + foreach ( $this->theme_list as $i => &$theme ) + { + $theme['css'] = array(); + $dir = ENANO_ROOT . "/themes/{$theme['theme_id']}/css"; + if ( $dh = @opendir($dir) ) + { + while ( ( $file = @readdir($dh) ) !== false ) + { + if ( preg_match('/\.css$/', $file) ) + $theme['css'][] = preg_replace('/\.css$/', '', $file); } closedir($dh); } + // No CSS files? If so, nuke it. + if ( count($theme['css']) < 1 ) + { + unset($this->theme_list[$i]); + } + } + $this->theme_list = array_values($this->theme_list); + // Create associative array of themes + foreach ( $this->theme_list as $i => &$theme ) + $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i]; + + $this->default_theme = ( $_ = getConfig('theme_default') ) ? $_ : $this->theme_list[0]['theme_id']; + // Come up with the default style. If the CSS file specified in default_style exists, we're good, just + // use that. Otherwise, use the first stylesheet that comes to mind. + $df_data =& $this->named_theme_list[ $this->default_theme ]; + $this->default_style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0]; + } + + /** + * Systematically deletes themes if they're blocked by theme security settings. Called when session->start() finishes. + */ + + function process_theme_acls() + { + global $db, $session, $paths, $template, $plugins; // Common objects + + // For each theme, check ACLs and delete from RAM if not authorized + foreach ( $this->theme_list as $i => $theme ) + { + if ( !$theme['group_list'] ) + continue; + switch ( $theme['group_policy'] ) + { + case 'allow_all': + // Unconditionally allowed + continue; + break; + case 'whitelist': + // If we're not on the list, off to the left please + $list = enano_json_decode($theme['group_list']); + $allowed = false; + foreach ( $list as $acl ) + { + if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) ) + // Invalid list entry, silently allow (maybe not a good idea but + // really, these things are checked before they're inserted) + continue 2; + $mode = $match[1]; + $id = intval($match[2]); + switch ( $mode ) + { + case 'u': + $allowed = ( $id == $session->user_id ); + if ( $allowed ) + break 2; + break; + case 'g': + $allowed = ( isset($session->groups[$id]) ); + if ( $allowed ) + break 2; + } + } + if ( !$allowed ) + { + unset($this->theme_list[$i]); + } + break; + case 'blacklist': + // If we're ON the list, off to the left please + $list = enano_json_decode($theme['group_list']); + $allowed = true; + foreach ( $list as $acl ) + { + if ( !preg_match('/^(u|g):([0-9]+)$/', $acl, $match) ) + // Invalid list entry, silently allow (maybe not a good idea but + // really, these things are checked before they're inserted) + continue 2; + $mode = $match[1]; + $id = intval($match[2]); + switch ( $mode ) + { + case 'u': + $allowed = ( $id != $session->user_id ); + if ( !$allowed ) + break 2; + break; + case 'g': + $allowed = ( !isset($session->groups[$id]) ); + if ( !$allowed ) + break 2; + } + } + if ( !$allowed ) + { + unset($this->theme_list[$i]); + } + break; + } } - $def = ENANO_ROOT.'/themes/'.$this->default_theme.'/css/'.$this->named_theme_list[$this->default_theme]['default_style']; - if(file_exists($def)) - { - $this->default_style = substr($this->named_theme_list[$this->default_theme]['default_style'], 0, strlen($this->named_theme_list[$this->default_theme]['default_style'])-4); - } else { - $this->default_style = $list[0]; - } + $this->theme_list = array_values($this->theme_list); - $this->style_list = $list; - + // Rebuild associative theme list + $this->named_theme_list = array(); + foreach ( $this->theme_list as $i => &$theme ) + $this->named_theme_list[ $theme['theme_id'] ] =& $this->theme_list[$i]; } + function sidebar_widget($t, $h, $use_normal_section = false) { global $db, $session, $paths, $template, $plugins; // Common objects @@ -120,7 +227,7 @@ echo "/* WARNING: Falling back to default file because file $path does not exist */\n"; $path = 'css/' . $this->style_list[0] . '.css'; } - return $this->process_template($path); + return '' . $this->process_template($path) . ''; } function load_theme($name = false, $css = false) { @@ -132,6 +239,26 @@ $this->theme = $this->theme_list[0]['theme_id']; $this->style = preg_replace('/\.css$/', '', $this->theme_list[0]['default_style']); } + // Make sure we're allowed to use this theme. + if ( ( + // If it was removed, it's probably blocked by an ACL, or it was uninstalled + !isset($this->named_theme_list[$this->theme]) || + // Check if the theme is disabled + ( isset($this->named_theme_list[$this->theme]) && $this->named_theme_list[$this->theme]['enabled'] == 0 ) ) + // Above all, if it's a system theme, don't inhibit the loading process. + && !in_array($this->theme, $this->system_themes) + ) + { + // No, something is preventing it - fall back to site default + $this->theme = $this->default_theme; + + // Come up with the default style. If the CSS file specified in default_style exists, we're good, just + // use that. Otherwise, use the first stylesheet that comes to mind. + $df_data =& $this->named_theme_list[ $this->theme ]; + $this->style = ( in_array($df_data['default_style'], $df_data['css']) ) ? $df_data['default_style'] : $df_data['css'][0]; + } + // The list of styles for the currently selected theme + $this->style_list =& $this->named_theme_list[ $this->theme ]['css']; $this->theme_loaded = true; } diff -r a044ad834691 -r 7906fb190fc1 plugins/admin/ThemeManager.php --- a/plugins/admin/ThemeManager.php Sat Mar 01 19:01:07 2008 -0500 +++ b/plugins/admin/ThemeManager.php Sat Mar 01 23:02:05 2008 -0500 @@ -24,7 +24,7 @@ return; } - $system_themes = array('admin', 'printable'); + $system_themes =& $template->system_themes; // Obtain the list of themes (both available and already installed) and the styles available for each $dh = @opendir(ENANO_ROOT . '/themes'); @@ -310,7 +310,7 @@ if ( $enable == 0 && ( $theme_default === $theme_data['theme_id'] || $theme_data['make_default'] ) ) { $enable = '1'; - $warn_default .= $lang->get('acptm_warn_cant_disable_default'); + $warn_default .= '' . $lang->get('acptm_warn_cant_disable_default') . ''; } // We're good. Update the theme...