* @author Frank Schubert */ // must be run within DokuWiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'admin.php'); /** * All DokuWiki plugins to extend the admin function * need to inherit from this class */ class admin_plugin_superacl extends DokuWiki_Admin_Plugin { function admin_plugin_superacl(){ $this->setupLocale(); } /** * return some info */ function getInfo(){ return array( 'author' => 'Pascal Bihler', 'email' => 'bihler@iai.uni-bonn.de', 'date' => '2008-03-18', 'name' => 'SuperACL', 'desc' => 'Manage Access Control Lists for all workspaces', 'url' => 'http://www.dokuwiki.org/plugin:superacl', ); } /** * access for managers */ function forAdminOnly(){ return true; // set to false to allow Manager access to Super ACL } /** * return prompt for admin menu */ function getMenuText($language) { return $this->lang['admin_acl']; } /** * return sort order for position in admin menu */ function getMenuSort() { return 1; } /** * handle user request */ function handle() { global $AUTH_ACL; $cmd = $_REQUEST['acl_cmd']; $scope = $_REQUEST['acl_scope']; $type = $_REQUEST['acl_type']; $user = $_REQUEST['acl_user']; $perm = $_REQUEST['acl_perm']; if(is_array($perm)){ //use the maximum sort($perm); $perm = array_pop($perm); }else{ $perm = 0; } //sanitize $user = auth_nameencode($user); if($type == '@') $user = '@'.$user; if($user == '@all') $user = '@ALL'; //special group! (now case insensitive) $perm = (int) $perm; if($perm > AUTH_DELETE) $perm = AUTH_DELETE; //FIXME sanitize scope!!! //nothing to do? if(empty($cmd) || empty($scope) || empty($user)) return; if($cmd == 'save'){ $this->admin_acl_del($scope, $user); $this->admin_acl_add($scope, $user, $perm); }elseif($cmd == 'delete'){ $this->admin_acl_del($scope, $user); } // reload ACL config $AUTH_ACL = file(DOKU_CONF.'acl.auth.php'); } /** * ACL Output function * * print a table with all significant permissions for the * current id * * @author Frank Schubert * @author Andreas Gohr */ function html() { print $this->locale_xhtml('intro'); ptln('
'); ptln(''); //namespace selector $this->admin_superacl_select_ns(); //new $this->admin_superacl_html_new(); //current config $acls = $this->get_superacl_config($this->get_selected_ns()); foreach ($acls as $id => $acl){ $this->admin_superacl_html_current($id,$acl); } ptln('
'); ptln('
'); } /** * * Get current selected namespace (or namespace of $ID as alternative) * */ function get_selected_ns() { global $ID; $id = $_REQUEST['superacl_ns']; if (! $id) $id = getNS($ID); if (! $id) $id = '*'; return $id; } /** * * Get current selected acl_scope (or namespace of Selected NS as alternative) * */ function get_acl_scope() { $scope = $_REQUEST['acl_scope']; if (! $scope) $scope = $this->get_selected_ns() . ':*'; if (! $scope) $scope = '*'; return $scope; } /** * Get matching ACL lines for a namespace * * $id is namespacename, reads matching lines from $AUTH_ACL, * also reads ACLs from namespace * returns multi-array with key=pagename and value=array(user, acl) * * @author Pascal Bihler * @author Frank Schubert */ function get_superacl_config($id){ global $AUTH_ACL; $acl_config=array(); // match exact name $pages = $this->get_pages($id); foreach ($pages as $page_id) { if ($id != '*') $page_id = $id . ':' . $page_id; $matches = preg_grep('/^'.$page_id.'\s+.*/',$AUTH_ACL); if(count($matches)){ foreach($matches as $match){ $match = preg_replace('/#.*$/','',$match); //ignore comments $acl = preg_split('/\s+/',$match); //0 is pagename, 1 is user, 2 is acl $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); } } } $specific_found=array(); // match ns for(;$id !== false; $id = getNS($id)){ $id_pattern = str_replace('+', '\\+',str_replace('*', '\\*', $id)); $matches = preg_grep('/^'.$id_pattern.':\*\s+.*/',$AUTH_ACL); if(count($matches)){ foreach($matches as $match){ $match = preg_replace('/#.*$/','',$match); //ignore comments $acl = preg_split('/\s+/',$match); //0 is pagename, 1 is user, 2 is acl $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); $specific_found[]=$acl[1]; } } } //include *-config $matches = preg_grep('/^\*\s+.*/',$AUTH_ACL); if(count($matches)){ foreach($matches as $match){ $match = preg_replace('/#.*$/','',$match); //ignore comments $acl = preg_split('/\s+/',$match); // only include * for this user if not already found in ns if(!in_array($acl[1], $specific_found)){ //0 is pagename, 1 is user, 2 is acl $acl_config[$acl[0]][] = array( 'name' => $acl[1], 'perm' => $acl[2]); } } } //sort //FIXME: better sort algo: first sort by key, then sort by first value krsort($acl_config, SORT_STRING); return($acl_config); } /** * adds new acl-entry to conf/acl.auth.php * * @author Frank Schubert */ function admin_acl_add($acl_scope, $acl_user, $acl_level){ $acl_config = join("",file(DOKU_CONF.'acl.auth.php')); // max level for pagenames is edit if(strpos($acl_scope,'*') === false) { if($acl_level > AUTH_EDIT) $acl_level = AUTH_EDIT; } $new_acl = "$acl_scope\t$acl_user\t$acl_level\n"; $new_config = $acl_config.$new_acl; return io_saveFile(DOKU_CONF.'acl.auth.php', $new_config); } /** * remove acl-entry from conf/acl.auth.php * * @author Frank Schubert */ function admin_acl_del($acl_scope, $acl_user){ $acl_config = file(DOKU_CONF.'acl.auth.php'); $acl_pattern = '^'.preg_quote($acl_scope,'/').'\s+'.$acl_user.'\s+[0-8].*$'; // save all non!-matching #FIXME invert is available from 4.2.0 only! $new_config = preg_grep("/$acl_pattern/", $acl_config, PREG_GREP_INVERT); return io_saveFile(DOKU_CONF.'acl.auth.php', join('',$new_config)); } // --- HTML OUTPUT FUNCTIONS BELOW --- // /** * create dropdown with parent namespaces and pages of one namespace ID * * * * @author Pascal Bihler * @author Frank Schubert * @author Andreas Gohr */ function admin_superacl_html_dropdown($id){ $cur = $id; $ret = ''; $selected_id = $this->get_acl_scope(); $opt = array(); //prepare all options (in reversed order) $pages = array(); if ($this->getConf('use_ajax')) { $ret .= sprintf('',$id); $ret .= sprintf('',$selected_id); $ret .= sprintf('',$this->lang['page']); $ret .= sprintf('',$this->lang['namespace']); } else { // pages in this namespace $pages = array_reverse($this->get_pages($id)); } // add pages in list foreach ($pages as $page_id) { $page_id = ($id != '*' ? $id . ':' : '') . $page_id; $opt[] = array( 'value'=> $page_id, 'text'=> $page_id.' ('.$this->lang['page'].')' ); if ($page_id == $selected_id) $opt[count($opt)-1]['sel'] = true; } // add selected page (if not in list above) if (!($pages) && ($this->is_page($selected_id))) { $opt[] = array( 'value'=> $selected_id, 'text'=> $selected_id.' ('.$this->lang['page'].')', 'sel' => true ); } // additional namespaces for(; $id !== false && $id != '*'; $id=getNS($id)){ $opt[] = array('value'=> $id.':*', 'text'=> $id.':* ('.$this->lang['namespace'].')'); if ($id.':*' == $selected_id) $opt[count($opt)-1]['sel'] = true; } // the top namespace $opt[] = array( 'value'=> '*', 'text'=> '* ('.$this->lang['namespace'].')' ); // flip options $opt = array_reverse($opt); // create HTML $att = array( 'name' => 'acl_scope', 'id' => 'superacl__pageselect', 'class' => 'edit', 'title' => $this->lang['page'].'/'.$this->lang['namespace'] ); $ret .= ''; return $ret; } /** * Decides, if a id is a page * */ function is_page($id){ return (id != "") && ! (substr($id,-1) == '*'); } /** * creates dropdown with all namespaces * * @author Pascal Bihler */ function admin_superacl_html_ns_dropdown($id){ global $ID; $ret = ''; $opt = array(); $namespaces = array(); if ($this->getConf('use_ajax')) { $ret .= sprintf('',$ID); $ret .= sprintf('',$id); } else { $namespaces = $this->get_namespaces(); } // add namespaces of current page to the list of namespaces (even if the namespaces doesn't exist yet for($ns_id = getNS($ID); $ns_id !== false; $ns_id = getNS($ns_id)){ if (!in_array($ns_id,$namespaces)) $namespaces[] = $ns_id; } // add selected namespace, if not in list above if ($id && !in_array($id,$namespaces)) $namespaces[] = $id; sort($namespaces); $namespaces = array_reverse($namespaces); foreach ($namespaces as $ns_id) { if ($ns_id == "*") continue; $opt[] = array('value'=> $ns_id, 'text'=> $ns_id.':*'); if ($ns_id == $id) { // set sel on current selected namespcase $opt[count($opt)-1]['sel'] = true; } } $opt[] = array('value'=> '*', 'text'=> '*'); // flip options $opt = array_reverse($opt); // create HTML $att = array( 'name' => 'superacl_ns', 'id' => 'superacl__nsselect', 'class' => 'edit', 'title' => $this->lang['page'].'/'.$this->lang['namespace'] ); $ret .= ''; return $ret; } /** * print form to select namespace to modify * * @author Pascal Bihler * */ function admin_superacl_select_ns() { global $ID; global $lang; // table headers ptln('',2); ptln(' '.$this->lang['acl_select'].'',2); ptln('',2); ptln('',2); ptln('',4); ptln('
',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); //scope select ptln($this->lang['acl_perms'],4); ptln($this->admin_superacl_html_ns_dropdown($this->get_selected_ns()),4); ptln(' ',4); ptln('
'); ptln('',4); ptln('',2); } /** * print form to add new Permissions * * @author Pascal Bihler * @author Frank Schubert * @author Andreas Gohr */ function admin_superacl_html_new(){ global $ID; global $lang; // table headers ptln('',2); ptln(' '.$this->lang['acl_new'].'',2); ptln('',2); ptln('',2); ptln('',4); ptln('
',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); //scope select ptln($this->lang['acl_perms'],4); ptln($this->admin_superacl_html_dropdown($this->get_selected_ns()),4); $att = array( 'name' => 'acl_type', 'class' => 'edit', 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group'] ); ptln(' ',4); $att = array( 'name' => 'acl_user', 'type' => 'text', 'class' => 'edit', 'title' => $this->lang['acl_user'].'/'.$this->lang['acl_group'] ); ptln(' ',4); ptln('
'); ptln( $this->admin_acl_html_checkboxes(0,false),8); ptln(' ',4); ptln('
'); ptln('',4); ptln('',2); } /** * print tablerows with the current permissions for one id * * @author Pascal Bihler * @author Frank Schubert * @author Andreas Gohr */ function admin_superacl_html_current($id,$permissions){ global $lang; global $ID; //is it a page? $ispage = $this->is_page($id); // table headers ptln(' '); ptln(' '); ptln($this->lang['acl_perms'],6); if($ispage){ ptln($this->lang['page'],6); }else{ ptln($this->lang['namespace'],6); } ptln(''.$id.'',6); ptln(' '); ptln(' '); sort($permissions); foreach ($permissions as $conf){ //userfriendly group/user display $conf['name'] = rawurldecode($conf['name']); if(substr($conf['name'],0,1)=="@"){ $group = $this->lang['acl_group']; $name = substr($conf['name'],1); $type = '@'; }else{ $group = $this->lang['acl_user']; $name = $conf['name']; $type = ''; } ptln('',2); ptln(''.htmlspecialchars($group.' '.$name).'',4); // update form ptln('',4); ptln('
',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln( $this->admin_acl_html_checkboxes($conf['perm'],$ispage),8); ptln(' ',4); ptln('
'); ptln('',4); // deletion form $ask = $lang['del_confirm'].'\\n'; $ask .= $id.' '.$conf['name'].' '.$conf['perm']; ptln('',4); ptln('
',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln(' ',4); ptln('
',4); ptln('',4); ptln('',2); } } /** * print the permission checkboxes * * @author Frank Schubert * @author Andreas Gohr */ function admin_acl_html_checkboxes($setperm,$ispage){ global $lang; static $label = 0; //number labels $ret = ''; foreach(array(AUTH_READ,AUTH_EDIT,AUTH_CREATE,AUTH_UPLOAD,AUTH_DELETE) as $perm){ $label += 1; //general checkbox attributes $atts = array( 'type' => 'checkbox', 'id' => 'pbox'.$label, 'name' => 'acl_perm[]', 'value' => $perm ); //dynamic attributes if($setperm >= $perm) $atts['checked'] = 'checked'; $atts['onchange'] = "superacl_autoselect_permissions(this);"; if($ispage && $perm > AUTH_EDIT) $atts['disabled'] = 'disabled'; //build code $ret .= '\n"; } return $ret; } function get_pages($tns='') { return $this->_getlist($tns,false,false,true); } function get_namespaces($tns='') { return $this->_getlist($tns,true,true,false); } /** * inspired from addnewpageplugin: */ function _getlist ($tns='',$recursive = true, $namespaces=true,$pages=false) { require_once(DOKU_INC.'inc/search.php'); global $conf; if ($tns == '*') $tns = ''; if (!is_dir($tns)) $tns = str_replace(':','/',$tns); $data = array(); search($data,$conf['datadir'] ."/" . $tns,'search_index',array('ns' => '')); $data2 = array(); foreach($data as $k => $v) { if ($v['type']=='d') { //Namespace if ($namespaces) array_push($data2,$v['id']); if ($recursive) { $r=$this->_getlist($tns.'/'.$v['id'],$recursive,$namespaces,$pages); foreach ($r as $vv) { array_push($data2,$v['id'].':'.$vv); } } } elseif ($v['type']=='f') { //Page if ($pages) array_push($data2,$v['id']); } } return $data2; } }