/**
  * Adds a new acl-entry to conf/acl.auth.php, as well as lib/plugins/daftdrafts/daft.auth.php.
  *
  * @author Jon Magne Bøe <*****@*****.**>
  * @author Frank Schubert <*****@*****.**>
  * @author Gerry Weissbach <*****@*****.**>
  */
 function acl_add($acl_scope, $acl_user, $acl_level, $additional = 'DAFTDRAFTS')
 {
     global $AUTH_ACL;
     $acl_config = $AUTH_ACL;
     if (empty($acl_config)) {
         $acl_config = file(DOKU_CONF . 'acl.auth.php');
     }
     $daftAcl = file(dirname(__FILE__) . '/daft.auth.php');
     $acl_user = auth_nameencode($acl_user, true);
     if (empty($acl_user)) {
         return false;
     }
     if (!empty($additional)) {
         $additionalCheck = '\\t#' . $additional;
         $additional = "\t#" . $additional;
     }
     //Checks that the acl_level is not higher than the permitted maximum for pages:
     if (strpos($acl_scope, '*') === false) {
         if ($acl_level > AUTH_EDIT) {
             $acl_level = AUTH_EDIT;
         }
     }
     $existInDaft = false;
     $existInAcl = false;
     $acl_pattern = '^' . preg_quote($acl_scope, '/') . '\\s+' . $acl_user . '\\s+[0-8].*' . $additionalCheck . '$';
     $acl_pattern_nocomment = '^' . preg_quote($acl_scope, '/') . '\\s+' . $acl_user . '\\s+[0-8].*$';
     //Checks if this exists in daftAcl:
     if (preg_grep("/{$acl_pattern}/", $daftAcl)) {
         if (preg_grep("/{$acl_pattern_nocomment}/", $acl_config)) {
             return true;
         } else {
             $existInDaft = true;
         }
     } elseif (preg_grep("/{$acl_pattern_nocomment}/", $acl_config)) {
         $existInAcl = true;
     }
     if (!$existInDaft) {
         $daftAcl[] = "{$acl_scope}\t{$acl_user}\t{$acl_level}{$additional}\n";
         //Adds acl-info to daftAcl
         io_saveFile(dirname(__FILE__) . '/daft.auth.php', join('', $daftAcl));
     }
     if (!$existInAcl) {
         $acl_config[] = "{$acl_scope}\t{$acl_user}\t{$acl_level}{$additional}\n";
         //Adds acl-info to acl_config
         $AUTH_ACL = $acl_config;
         io_saveFile(DOKU_CONF . 'acl.auth.php', join('', $acl_config));
     }
     return true;
     //The function does not check if the files were actually saved.
 }
Exemple #2
0
/**
 * Returns the maximum rights a user has for
 * the given ID or its namespace
 *
 * @author  Andreas Gohr <*****@*****.**>
 *
 * @param  string       $id     page ID (needs to be resolved and cleaned)
 * @param  string       $user   Username
 * @param  array|null   $groups Array of groups the user is in
 * @return int             permission level
 */
function auth_aclcheck($id, $user, $groups)
{
    global $conf;
    global $AUTH_ACL;
    /* @var auth_basic $auth */
    global $auth;
    // for PART-DB
    return !$conf['useacl'] && file_exists(DOKU_INC . '../../data/ENABLE-DOKUWIKI-WRITE-PERMS.txt') ? AUTH_ADMIN : AUTH_READ;
    // if no ACL is used always return upload rights
    if (!$conf['useacl']) {
        return AUTH_UPLOAD;
    }
    if (!$auth) {
        return AUTH_NONE;
    }
    //make sure groups is an array
    if (!is_array($groups)) {
        $groups = array();
    }
    //if user is superuser or in superusergroup return 255 (acl_admin)
    if (auth_isadmin($user, $groups)) {
        return AUTH_ADMIN;
    }
    $ci = '';
    if (!$auth->isCaseSensitive()) {
        $ci = 'ui';
    }
    $user = $auth->cleanUser($user);
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
    $user = auth_nameencode($user);
    //prepend groups with @ and nameencode
    $cnt = count($groups);
    for ($i = 0; $i < $cnt; $i++) {
        $groups[$i] = '@' . auth_nameencode($groups[$i]);
    }
    $ns = getNS($id);
    $perm = -1;
    if ($user || count($groups)) {
        //add ALL group
        $groups[] = '@ALL';
        //add User
        if ($user) {
            $groups[] = $user;
        }
    } else {
        $groups[] = '@ALL';
    }
    //check exact match first
    $matches = preg_grep('/^' . preg_quote($id, '/') . '\\s+(\\S+)\\s+/' . $ci, $AUTH_ACL);
    if (count($matches)) {
        foreach ($matches as $match) {
            $match = preg_replace('/#.*$/', '', $match);
            //ignore comments
            $acl = preg_split('/\\s+/', $match);
            if (!in_array($acl[1], $groups)) {
                continue;
            }
            if ($acl[2] > AUTH_DELETE) {
                $acl[2] = AUTH_DELETE;
            }
            //no admins in the ACL!
            if ($acl[2] > $perm) {
                $perm = $acl[2];
            }
        }
        if ($perm > -1) {
            //we had a match - return it
            return $perm;
        }
    }
    //still here? do the namespace checks
    if ($ns) {
        $path = $ns . ':*';
    } else {
        $path = '*';
        //root document
    }
    do {
        $matches = preg_grep('/^' . preg_quote($path, '/') . '\\s+(\\S+)\\s+/' . $ci, $AUTH_ACL);
        if (count($matches)) {
            foreach ($matches as $match) {
                $match = preg_replace('/#.*$/', '', $match);
                //ignore comments
                $acl = preg_split('/\\s+/', $match);
                if (!in_array($acl[1], $groups)) {
                    continue;
                }
                if ($acl[2] > AUTH_DELETE) {
                    $acl[2] = AUTH_DELETE;
                }
                //no admins in the ACL!
                if ($acl[2] > $perm) {
                    $perm = $acl[2];
                }
            }
            //we had a match - return it
            if ($perm != -1) {
                return $perm;
            }
        }
        //get next higher namespace
        $ns = getNS($ns);
        if ($path != '*') {
            $path = $ns . ':*';
            if ($path == ':*') {
                $path = '*';
            }
        } else {
            //we did this already
            //looks like there is something wrong with the ACL
            //break here
            msg('No ACL setup yet! Denying access to everyone.');
            return AUTH_NONE;
        }
    } while (1);
    //this should never loop endless
    return AUTH_NONE;
}
 /**
  * Adds a new subscription for the given page or namespace
  *
  * This will automatically overwrite any existent subscription for the given user on this
  * *exact* page or namespace. It will *not* modify any subscription that may exist in higher namespaces.
  *
  * @param string $id The target page or namespace, specified by id; Namespaces
  *                   are identified by appending a colon.
  * @param string $user
  * @param string $style
  * @param string $data
  * @throws Exception when user or style is empty
  * @return bool
  */
 public function add($id, $user, $style, $data = '')
 {
     if (!$this->isenabled()) {
         return false;
     }
     // delete any existing subscription
     $this->remove($id, $user);
     $user = auth_nameencode(trim($user));
     $style = trim($style);
     $data = trim($data);
     if (!$user) {
         throw new Exception('no subscription user given');
     }
     if (!$style) {
         throw new Exception('no subscription style given');
     }
     if (!$data) {
         $data = time();
     }
     //always add current time for new subscriptions
     $line = "{$user} {$style} {$data}\n";
     $file = $this->file($id);
     return io_saveFile($file, $line, true);
 }
Exemple #4
0
 /**
  * remove acl-entry from conf/acl.auth.php
  *
  * @author  Frank Schubert <*****@*****.**>
  */
 function _acl_del($acl_scope, $acl_user)
 {
     global $config_cascade;
     $acl_config = file($config_cascade['acl']['default']);
     $acl_user = auth_nameencode($acl_user, true);
     $acl_pattern = '^' . preg_quote($acl_scope, '/') . '\\s+' . $acl_user . '\\s+[0-8].*$';
     // save all non!-matching
     $new_config = preg_grep("/{$acl_pattern}/", $acl_config, PREG_GREP_INVERT);
     return io_saveFile($config_cascade['acl']['default'], join('', $new_config));
 }
Exemple #5
0
/**
 * default ACL check method
 *
 * DO NOT CALL DIRECTLY, use auth_aclcheck() instead
 *
 * @author  Andreas Gohr <*****@*****.**>
 * @param  array $data event data
 * @return int   permission level
 */
function auth_aclcheck_cb($data)
{
    $id =& $data['id'];
    $user =& $data['user'];
    $groups =& $data['groups'];
    global $conf;
    global $AUTH_ACL;
    /* @var DokuWiki_Auth_Plugin $auth */
    global $auth;
    // if no ACL is used always return upload rights
    if (!$conf['useacl']) {
        return AUTH_UPLOAD;
    }
    if (!$auth) {
        return AUTH_NONE;
    }
    //make sure groups is an array
    if (!is_array($groups)) {
        $groups = array();
    }
    //if user is superuser or in superusergroup return 255 (acl_admin)
    if (auth_isadmin($user, $groups)) {
        return AUTH_ADMIN;
    }
    if (!$auth->isCaseSensitive()) {
        $user = utf8_strtolower($user);
        $groups = array_map('utf8_strtolower', $groups);
    }
    $user = $auth->cleanUser($user);
    $groups = array_map(array($auth, 'cleanGroup'), (array) $groups);
    $user = auth_nameencode($user);
    //prepend groups with @ and nameencode
    $cnt = count($groups);
    for ($i = 0; $i < $cnt; $i++) {
        $groups[$i] = '@' . auth_nameencode($groups[$i]);
    }
    $ns = getNS($id);
    $perm = -1;
    if ($user || count($groups)) {
        //add ALL group
        $groups[] = '@ALL';
        //add User
        if ($user) {
            $groups[] = $user;
        }
    } else {
        $groups[] = '@ALL';
    }
    //check exact match first
    $matches = preg_grep('/^' . preg_quote($id, '/') . '[ \\t]+([^ \\t]+)[ \\t]+/', $AUTH_ACL);
    if (count($matches)) {
        foreach ($matches as $match) {
            $match = preg_replace('/#.*$/', '', $match);
            //ignore comments
            $acl = preg_split('/[ \\t]+/', $match);
            if (!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
                $acl[1] = utf8_strtolower($acl[1]);
            }
            if (!in_array($acl[1], $groups)) {
                continue;
            }
            if ($acl[2] > AUTH_DELETE) {
                $acl[2] = AUTH_DELETE;
            }
            //no admins in the ACL!
            if ($acl[2] > $perm) {
                $perm = $acl[2];
            }
        }
        if ($perm > -1) {
            //we had a match - return it
            return (int) $perm;
        }
    }
    //still here? do the namespace checks
    if ($ns) {
        $path = $ns . ':*';
    } else {
        $path = '*';
        //root document
    }
    do {
        $matches = preg_grep('/^' . preg_quote($path, '/') . '[ \\t]+([^ \\t]+)[ \\t]+/', $AUTH_ACL);
        if (count($matches)) {
            foreach ($matches as $match) {
                $match = preg_replace('/#.*$/', '', $match);
                //ignore comments
                $acl = preg_split('/[ \\t]+/', $match);
                if (!$auth->isCaseSensitive() && $acl[1] !== '@ALL') {
                    $acl[1] = utf8_strtolower($acl[1]);
                }
                if (!in_array($acl[1], $groups)) {
                    continue;
                }
                if ($acl[2] > AUTH_DELETE) {
                    $acl[2] = AUTH_DELETE;
                }
                //no admins in the ACL!
                if ($acl[2] > $perm) {
                    $perm = $acl[2];
                }
            }
            //we had a match - return it
            if ($perm != -1) {
                return (int) $perm;
            }
        }
        //get next higher namespace
        $ns = getNS($ns);
        if ($path != '*') {
            $path = $ns . ':*';
            if ($path == ':*') {
                $path = '*';
            }
        } else {
            //we did this already
            //looks like there is something wrong with the ACL
            //break here
            msg('No ACL setup yet! Denying access to everyone.');
            return AUTH_NONE;
        }
    } while (1);
    //this should never loop endless
    return AUTH_NONE;
}
Exemple #6
0
/**
 * Set subscription information
 *
 * Allows to set subscription information for permanent storage in meta files.
 * Subscriptions consist of a target object, a subscribing user, a subscribe
 * style and optional data.
 * A subscription may be deleted by specifying an empty subscribe style.
 * Only one subscription per target and user is allowed.
 * The function returns false on error, otherwise true. Note that no error is
 * returned if a subscription should be deleted but the user is not subscribed
 * and the subscription meta file exists.
 *
 * @param string $user      The subscriber or unsubscriber
 * @param string $page      The target object (page or namespace), specified by
 *                          id; Namespaces are identified by a trailing colon.
 * @param string $style     The subscribe style; DokuWiki currently implements
 *                          “every”, “digest”, and “list”.
 * @param string $data      An optional data blob
 * @param bool   $overwrite Whether an existing subscription may be overwritten
 *
 * @author Adrian Lang <*****@*****.**>
 */
function subscription_set($user, $page, $style, $data = null, $overwrite = false)
{
    global $lang;
    if (is_null($style)) {
        // Delete subscription.
        $file = subscription_filename($page);
        if (!@file_exists($file)) {
            msg(sprintf($lang['subscr_not_subscribed'], $user, prettyprint_id($page)), -1);
            return false;
        }
        // io_deleteFromFile does not return false if no line matched.
        return io_deleteFromFile($file, subscription_regex(array('user' => auth_nameencode($user))), true);
    }
    // Delete subscription if one exists and $overwrite is true. If $overwrite
    // is false, fail.
    $subs = subscription_find($page, array('user' => $user));
    if (count($subs) > 0 && array_pop(array_keys($subs)) === $page) {
        if (!$overwrite) {
            msg(sprintf($lang['subscr_already_subscribed'], $user, prettyprint_id($page)), -1);
            return false;
        }
        // Fail if deletion failed, else continue.
        if (!subscription_set($user, $page, null)) {
            return false;
        }
    }
    $file = subscription_filename($page);
    $content = auth_nameencode($user) . ' ' . $style;
    if (!is_null($data)) {
        $content .= ' ' . $data;
    }
    return io_saveFile($file, $content . "\n", true);
}
 /**
  *  Show a banner when viewing pages that are not published.
  *
  *  @author Jon Magne Bøe <*****@*****.**>
  */
 function show_banner(&$event, $param)
 {
     global $AUTH_ACL, $ID, $INFO;
     $autorizationList = $AUTH_ACL;
     $pageIdentification = $ID;
     $allUsers = auth_nameencode('@ALL', true);
     //all users
     $additional = 'DAFTDRAFTS';
     $autorizationList = $AUTH_ACL;
     if (empty($authorizationList)) {
         $authorizationList = file(DOKU_CONF . 'acl.auth.php');
     }
     $daftAuthorizationList = file(dirname(__FILE__) . '/daft.auth.php');
     $additional = '\\t#' . $additional;
     $acl_pattern_nocomment = '^' . preg_quote($pageIdentification, '/') . '\\s+.*\\s+[0-8].*$';
     //pattern for searching acl.auth.php
     $authorizationHits = preg_grep("/{$acl_pattern_nocomment}/", $authorizationList);
     $numberOfAuthorizations = count($authorizationHits);
     $daftHits = preg_grep("/{$acl_pattern_nocomment}/", $daftAuthorizationList);
     $numberOfDaftauthorizations = count($daftHits);
     //If both acl.auth.php and daft.auth.php contains restrictions of authorization, it's safe to assume it's not published.
     if ($numberOfAuthorizations != 0 && $numberOfDaftauthorizations != 0) {
         $published = false;
     } else {
         //Ideally the acl.auth.php and the daft.auth.php-files should be in sync, meaning we don't need to check anything else.
         $published = true;
     }
     $htmlcode = array();
     //This code runs if the page is unpublished, i.e. tagged as a draft and hidden from unregistered users.
     if (!$published && isset($INFO['userinfo'])) {
         $htmlcode[] = '<div class="unpublished">';
         $htmlcode[] = '<span class="draft">';
         $htmlcode[] = $this->getLang('unpublished');
         $htmlcode[] = '<br>';
         $htmlcode[] = $this->getLang('howtopublish') . ' ~~' . $this->getLang('code') . '~~';
         $htmlcode[] = '</span>';
         $htmlcode[] = '</div>';
         ptln(implode($htmlcode));
         return true;
     } else {
         return;
     }
 }
Exemple #8
0
 /**
  * remove acl-entry from conf/acl.auth.php
  *
  * @author  Frank Schubert <*****@*****.**>
  */
 function _acl_del($acl_scope, $acl_user)
 {
     $acl_config = file(DOKU_CONF . 'acl.auth.php');
     $acl_user = auth_nameencode($acl_user, true);
     $acl_pattern = '^' . preg_quote($acl_scope, '/') . '\\s+' . $acl_user . '\\s+[0-8].*$';
     // save all non!-matching
     $new_config = preg_grep("/{$acl_pattern}/", $acl_config, PREG_GREP_INVERT);
     return io_saveFile(DOKU_CONF . 'acl.auth.php', join('', $new_config));
 }
Exemple #9
0
 function handle_magicacl2(&$event, $param)
 {
     $debug = true;
     global $ID;
     global $AUTH_ACL;
     global $INFO;
     global $USERINFO;
     $dodebug = false;
     if ($debug && $_GET['doit']) {
         $dodebug = true;
     }
     //print "DEBUG: ". $_SERVER['REMOTE_ADDR'];
     //      if($_SERVER['REMOTE_ADDR'] == '134.84.199.3') {
     //        $dodebug = true;
     //      }
     $ns = $event->data['ns'];
     if ($dodebug) {
         print "<pre>\n\n";
         print "physmagicACL pluggin hook running. ns={$ns}\n";
     }
     $areas = $this->getConf('areas');
     if (!count($areas)) {
         return;
     }
     foreach ($areas as $magicns => $area) {
         //$magicns = $area['ns_base'];
         $level = $area['level'];
         $cust_acl = $area['acl'];
         //$perm = $area['perm_level'];
         //$groupbase = $area['group_base'];
         if ($dodebug) {
             print "Looking at physmagicacl config area:{$magicns}\n";
         }
         if (!$cust_acl || !$level) {
             if ($dodebug) {
                 print "DEBUG: bailing because no cust_acl or no level\n";
             }
             continue;
         }
         $namespaces = array();
         // continue only if $magicns area is a substring of $ns and theres no weird chars in $ns
         if (preg_match("/^{$magicns}/", $ns) && count(preg_grep('/^[*:\\-\\.\\w]*$/', array($ns)))) {
             // Check for any groups that match the namespace
             $groups = $USERINFO['grps'];
             foreach ($groups as $group) {
                 $area_slice = explode('_', $group, $level);
                 if (count($area_slice) == $level && $area_slice[0] == $magicns) {
                     $namespace['ns'] = implode(':', $area_slice);
                     $namespace['group'] = $group;
                     if ($dodebug) {
                         print "Adding ns " . $namespace['ns'] . " and group {$group}\n";
                     }
                     $namespaces[] = $namespace;
                 }
             }
             // Take the current ns, explode it to the specified level
             // and implode it back to get the namespace and group for ACL
             $magicns = array_slice(explode(':', $ns), 0, $level);
             if ($dodebug) {
                 foreach ($magicns as $test) {
                     if ($test == '*') {
                         print "Yep, {$ns} has a * in it\n";
                     }
                 }
             }
             $rootns = implode(':', $magicns);
             $group = ($groupbase ? $groupbase : '') . implode('_', $magicns);
             // Update ACL
             if ($cust_acl) {
                 foreach ($cust_acl as $entry) {
                     if (!empty($entry)) {
                         $entryarr = preg_split('/\\s+/', $entry);
                         if ($dodebug) {
                             //print "entryarr: ";
                             //print_r($entryarr);
                         }
                         if (count($entryarr) >= 3) {
                             $AUTH_ACL[] = $rootns . $entryarr[0] . ' ' . auth_nameencode(str_replace('%GROUP%', $group, $entryarr[1]), true) . ' ' . $entryarr[2];
                             foreach ($namespaces as $aclns) {
                                 $AUTH_ACL[] = $aclns['ns'] . $entryarr[0] . ' ' . auth_nameencode(str_replace('%GROUP%', $aclns['group'], $entryarr[1]), true) . ' ' . $entryarr[2];
                             }
                         }
                     }
                 }
             }
             // If this isn't the mediamanager, we may need to update page permisions
         } else {
             if ($dodebug) {
                 print "Skipped area because its not in ns: {$ns} or ns is invalid\n";
             }
         }
     }
     if ($dodebug) {
         print '</pre><br>';
     }
 }
Exemple #10
0
/**
 * Loads the ACL setup and handle user wildcards
 *
 * @author Andreas Gohr <*****@*****.**>
 * @returns array
 */
function auth_loadACL($acl_file)
{
    global $config_cascade;
    $acl = $acl_file;
    $sess_id = session_id();
    if (!isset($sess_id) || $sess_id != $_COOKIE['FCK_NmSp_acl']) {
        session_id($_COOKIE['FCK_NmSp_acl']);
        session_start();
        if (isset($_SESSION['dwfck_client'])) {
            $_SERVER['REMOTE_USER'] = $_SESSION['dwfck_client'];
        }
    } else {
        if (isset($_SESSION['dwfck_client'])) {
            $_SERVER['REMOTE_USER'] = $_SESSION['dwfck_client'];
        }
    }
    //support user wildcard
    if (isset($_SERVER['REMOTE_USER'])) {
        $len = count($acl);
        for ($i = 0; $i < $len; $i++) {
            if ($acl[$i][0] == '#') {
                continue;
            }
            list($id, $rest) = preg_split('/\\s+/', $acl[$i], 2);
            $id = str_replace('%USER%', cleanID($_SERVER['REMOTE_USER']), $id);
            $rest = str_replace('%USER%', auth_nameencode($_SERVER['REMOTE_USER']), $rest);
            $acl[$i] = "{$id}\t{$rest}";
        }
    } else {
        $acl = str_replace('%USER%', $user, $acl);
        // fall-back, in case client not found
    }
    return $acl;
}
 function test_groupskipoff()
 {
     $in = '@hey$you';
     $out = '%40hey%24you';
     $this->assertEqual(auth_nameencode($in), $out);
 }
 function test_devanagiri()
 {
     $in = 'ut-fठ8';
     $expect = 'ut%2dfठ8';
     $this->assertEquals($expect, auth_nameencode($in));
 }
Exemple #13
0
 /**
  * remove acl-entry from conf/acl.auth.php
  *
  * @author  Frank Schubert <*****@*****.**>
  */
 function _acl_del($acl_scope, $acl_user)
 {
     global $config_cascade;
     $acl_user = auth_nameencode($acl_user, true);
     $acl_pattern = '^' . preg_quote($acl_scope, '/') . '[ \\t]+' . $acl_user . '[ \\t]+[0-8].*$';
     return io_deleteFromFile($config_cascade['acl']['default'], "/{$acl_pattern}/", true);
 }
Exemple #14
0
 /**
  * adds new acl-entry to conf/acl.auth.php
  *
  * @author  Frank Schubert <*****@*****.**>
  */
 function _acl_add($acl_scope, $acl_user)
 {
     $acl_config = loadModlist();
     $acl_user = auth_nameencode($acl_user, true);
     $new_acl = "{$acl_scope}\t{$acl_user}\n";
     $acl_config[] = $new_acl;
     return io_saveFile(DOKUTRANSLATE_MODLIST, join('', $acl_config));
 }
Exemple #15
0
 /**
  * remove acl-entry from conf/acl.auth.php
  *
  * @author  Frank Schubert <*****@*****.**>
  */
 function _acl_del($acl_scope, $acl_user)
 {
     $acl_config = $this->load_acl_config(false);
     $acl_user = auth_nameencode($acl_user, true);
     $acl_pattern = '^' . preg_quote($acl_scope, '/') . '\\s+' . $acl_user . '\\s+[0-8].*$';
     // save all non!-matching
     $new_config = preg_grep("/{$acl_pattern}/", $acl_config, PREG_GREP_INVERT);
     return $this->save_acl_config(join("\n", $new_config) . "\n");
 }
/**
 * Returns the maximum rights a user has for
 * the given ID or its namespace
 *
 * @author  Andreas Gohr <*****@*****.**>
 *
 * @param  string  $id     page ID
 * @param  string  $user   Username
 * @param  array   $groups Array of groups the user is in
 * @return int             permission level
 */
function auth_aclcheck($id, $user, $groups)
{
    global $conf;
    global $AUTH_ACL;
    // if no ACL is used always return upload rights
    if (!$conf['useacl']) {
        return AUTH_UPLOAD;
    }
    //make sure groups is an array
    if (!is_array($groups)) {
        $groups = array();
    }
    //if user is superuser or in superusergroup return 255 (acl_admin)
    if (auth_isadmin($user, $groups)) {
        return AUTH_ADMIN;
    }
    $user = auth_nameencode($user);
    //prepend groups with @ and nameencode
    $cnt = count($groups);
    for ($i = 0; $i < $cnt; $i++) {
        $groups[$i] = '@' . auth_nameencode($groups[$i]);
    }
    $ns = getNS($id);
    $perm = -1;
    if ($user || count($groups)) {
        //add ALL group
        $groups[] = '@ALL';
        //add User
        if ($user) {
            $groups[] = $user;
        }
        //build regexp
        $regexp = join('|', $groups);
    } else {
        $regexp = '@ALL';
    }
    //check exact match first
    $matches = preg_grep('/^' . preg_quote($id, '/') . '\\s+(' . $regexp . ')\\s+/', $AUTH_ACL);
    if (count($matches)) {
        foreach ($matches as $match) {
            $match = preg_replace('/#.*$/', '', $match);
            //ignore comments
            $acl = preg_split('/\\s+/', $match);
            if ($acl[2] > AUTH_DELETE) {
                $acl[2] = AUTH_DELETE;
            }
            //no admins in the ACL!
            if ($acl[2] > $perm) {
                $perm = $acl[2];
            }
        }
        if ($perm > -1) {
            //we had a match - return it
            return $perm;
        }
    }
    //still here? do the namespace checks
    if ($ns) {
        $path = $ns . ':\\*';
    } else {
        $path = '\\*';
        //root document
    }
    do {
        $matches = preg_grep('/^' . $path . '\\s+(' . $regexp . ')\\s+/', $AUTH_ACL);
        if (count($matches)) {
            foreach ($matches as $match) {
                $match = preg_replace('/#.*$/', '', $match);
                //ignore comments
                $acl = preg_split('/\\s+/', $match);
                if ($acl[2] > AUTH_DELETE) {
                    $acl[2] = AUTH_DELETE;
                }
                //no admins in the ACL!
                if ($acl[2] > $perm) {
                    $perm = $acl[2];
                }
            }
            //we had a match - return it
            return $perm;
        }
        //get next higher namespace
        $ns = getNS($ns);
        if ($path != '\\*') {
            $path = $ns . ':\\*';
            if ($path == ':\\*') {
                $path = '\\*';
            }
        } else {
            //we did this already
            //looks like there is something wrong with the ACL
            //break here
            msg('No ACL setup yet! Denying access to everyone.');
            return AUTH_NONE;
        }
    } while (1);
    //this should never loop endless
    //still here? return no permissions
    return AUTH_NONE;
}