DokuWiki

It's better when it's simple

User Tools

Site Tools


Sidebar

Translations of this page?:

Learn about DokuWiki

Advanced Use

Corporate Use

Our Community


Follow us on Facebook, Twitter and other social networks.

tips:ipacl

This small mod allows you to configure user access not just by username and group, but also by where a client is connecting from. The installation instructions below are for the 2005-07-13 release of dokuwiki. I will try to keep updating it for all the future releases as well, but ideally I would like to see this functionality make it into the main system.

How does it work?

The operation is rather simple. Use the ACLs to add a new rule either for a single IP, or for an entire network. Here's an example:

*     %192.168.1.0/24  1   # this rule gives any client on the 192.168.1.0/255.255.255.0 network read access to the whole wiki.
start %192.168.2.10    2   # this rule gives the client with the IP of 192.168.2.10 edit access to the home page.

Currently, I didn't modify the actual ACL control panel to include a new type of entry, so all of these rules are added in exactly the same way as you would add a new user. Simply prefix the IP or network with '%', chose what access rights they should have, and to what section of the wiki. Everything else works exactly the same way.

My system looks for all the entries that begin with % and tries to match them to the connecting user's IP. If an entry does not contain /xx on the end, it looks for an exact IP match. If there is a subnet mask at the end as in the example above, this mod will match only the part of the IP that fits within that mask, so an entry of 192.168.1.0/30 will give wiki access to 192.168.1.1, 192.168.1.2, and 192.168.1.3, but no one else. The subnet mask is a bit count, so for example 24 means “the top 24 bits of the 32 bit mask are one” (i.e. mask = 255.255.255.0). If you're not too familiar with the way subnet masking works, feel free to e-mail me for a better explanation.

I have not done too much testing on this as of yet, but it does seen to work so far just fine. If you find any problems with this mod, or just have any tips/suggestions/requests, feel free to e-mail me at max [at] mxserve [dot] net.

Install Instructions

The whole mod makes only 2 changes to inc/auth.php. Most of this code is actually a copy of the code that is used to check username and group access, I simply modified it to make it work with IPs.

  1. On line 307 (or somewhere around that), change this:
          //we did this already
          //looks like there is something wrong with the ACL
          //break here
          return $perm;

    to this:

          //we did this already
          //looks like there is something wrong with the ACL
          //break here
          //return $perm;
          break;
  2. After this line (should be 310):
          }while(1); //this should never loop endless

    add the following code:

      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      // IP ACL Mod - Max Khitrov <max@mxserve.net>
      // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     
      // First we get the IP of the client
      $user_ip = $_SERVER[REMOTE_ADDR];
     
      // Take all ACL entries that begin with % and see if they can be matched to
      // the client's ip (exact match first, namespace second).
      $matches = preg_grep('/^'.$id.'\s+(%[0-9\.\/]+)\s+/',$AUTH_ACL);
      if(count($matches)){
        foreach($matches as $match){
        	$match = preg_replace('/#.*$/','',$match); //ignore comments
        	if ($match == '')
        		continue;
     
        	$acl = preg_split('/\s+/',$match);
        	$acl[1] = substr($acl[1], 1, strlen($acl[1]) - 1);
     
        	if (strpos($acl[1], '/') === false) {
        		// This is an exact IP entry, see if it matches the user IP
        		if ($user_ip == $acl[1])
        			$perm = $acl[2];
        	} else {
        		// This is a network entry
        		$ip = preg_split('/\//',$acl[1]);
     
        		if ((ip2long($user_ip) & ~(pow(2, 32-$ip[1])-1)) == (ip2long($ip[0]) & ~(pow(2, 32-$ip[1])-1)))
        			$perm = $acl[2];
        	}
     
        	if($perm > -1){
          	//we had a match - return it
          	return $perm;
        	}
        }
      }
     
      $ns    = getNS($id);
      if($ns){
        $path = $ns.':\*';
      }else{
        $path = '\*'; //root document
      }
     
      do{
        $matches = preg_grep('/^'.$path.'\s+(%[0-9\.\/]+)\s+/',$AUTH_ACL);
        if(count($matches)){
          foreach($matches as $match){
            $match = preg_replace('/#.*$/','',$match); //ignore comments
            if ($match == '')
        			continue;
     
            $acl   = preg_split('/\s+/',$match);
            if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
     
            $acl[1] = substr($acl[1], 1, strlen($acl[1]) - 1);
     
    	    	if (strpos($acl[1], '/') === false) {
    	    		// This is an exact IP entry, see if it matches the user IP
    	    		if ($user_ip == $acl[1])
    	    			if($acl[2] > $perm)
    	    				$perm = $acl[2];
    	    	} else {
    	    		// This is a network entry
    	    		$ip = preg_split('/\//',$acl[1]);
     
    	    		if ((ip2long($user_ip) & ~(pow(2, 32-$ip[1])-1)) == (ip2long($ip[0]) & ~(pow(2, 32-$ip[1])-1)))
    	    			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
          return $perm;
        }
      }while(1); //this should never loop endless
     
      // ~~~~~~~~~~~~~~
      // END IP ACL Mod
      // ~~~~~~~~~~~~~~

Install Instructions for the 2006-03-09b release

In function auth_aclcheck, around line 321, immediately following the “do{” line, insert the code shown below.

It's slightly tricky to set up the permissions, because the main loop is trying to validate your identity (typically “@ALL”) and your IP address at the same time. The upshot is that to get wiki-wide access you have to specify your IP-based permissions in acl.auth.php for every namespace, not just “*”. Otherwise, if you have something like:

somenamespace:*	@ALL	0

It will override something like:

*     %192.168.1.1 1  # This IP gets read access to the whole wiki

That means you won't get read access in “somenamespace”, even if you're at IP 192.168.1.1.

Here's the code to insert after the “do{”.

  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // IP ACL Mod - Max Khitrov <max@mxserve.net>
  // Modified by Scott Gilbertson to work with a newer DokuWiki version
  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  // First we get the IP of the client
  $user_ip = $_SERVER[REMOTE_ADDR];
 
  // Take all ACL entries that begin with % and see if they can be matched to
  // the client's ip (exact match first, namespace second).
  $matches = preg_grep('/^'.$path.'\s+(%[0-9\.\/]+)\s+/',$AUTH_ACL);
  if(count($matches)){
    foreach($matches as $match){
    	$match = preg_replace('/#.*$/','',$match); //ignore comments
    	if ($match == '')
    		continue;
    	$acl = preg_split('/\s+/',$match);
    	$acl[1] = substr($acl[1], 1, strlen($acl[1]) - 1);
    	if (strpos($acl[1], '/') === false) {
    		// This is an exact IP entry, see if it matches the user IP
    		if ($user_ip == $acl[1]) {
    			$perm = $acl[2];
                }
    	} else {
    		// This is a network entry
    		$ip = preg_split('/\//',$acl[1]);
 
    		if ((ip2long($user_ip) & ~(pow(2, 32-$ip[1])-1)) == (ip2long($ip[0]) & ~(pow(2, 32-$ip[1])-1))) {
    			$perm = $acl[2];
                }
    	}
 
    	if($perm > -1){
      	//we had a match - return it
      	return $perm;
    	}
    }
  }
 
    $matches = preg_grep('/^'.$path.'\s+(%[0-9\.\/]+)\s+/',$AUTH_ACL);
    if(count($matches)){
      foreach($matches as $match){
        $match = preg_replace('/#.*$/','',$match); //ignore comments
        if ($match == '')
    			continue;
 
        $acl   = preg_split('/\s+/',$match);
        if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
 
        $acl[1] = substr($acl[1], 1, strlen($acl[1]) - 1);
 
	    	if (strpos($acl[1], '/') === false) {
	    		// This is an exact IP entry, see if it matches the user IP
	    		if ($user_ip == $acl[1])
	    			if($acl[2] > $perm)
	    				$perm = $acl[2];
	    	} else {
	    		// This is a network entry
	    		$ip = preg_split('/\//',$acl[1]);
 
	    		if ((ip2long($user_ip) & ~(pow(2, 32-$ip[1])-1)) == (ip2long($ip[0]) & ~(pow(2,32-$ip[1])-1)))
	    			if($acl[2] > $perm)
	    				$perm = $acl[2];
	    	}
      }
      //we had a match - return it
      //return $perm;
    }
 
  // ~~~~~~~~~~~~~~
  // END IP ACL Mod
  // ~~~~~~~~~~~~~~

Another (simpler) approach

If you don't need netmask support, but you can live with simpler class-network granularity, this approach might work for you. Here, every user automatically is member of four additional groups, based on his IP address. If he accesses for example from IP 123.45.67.89, he is by default member of following groups as well:

  • ip:123
  • ip:123.45
  • ip:123.45.67
  • ip:123.45.67.89

This means, if you want to give rights (only) to users from Apple, you would use the ACL Plugin to set the rights for the group “ip:17

To patch the 2010-11-07 version of dokuwiki, replace the lines 509-518 of inc/auth.php with the following code: (To patch the 2007-06-26 version, replace the lines 363-372)

  // --- patch to allow IP-Based access rights (replaces original lines 363-372) ----
  //add ALL group
  $groups[] = '@ALL';
  //add IP-based groups:
  $ip = explode('.',$_SERVER["REMOTE_ADDR"]);
  $groups[] = '@ip%3a' . $ip[0] . '%2e' . $ip[1] . '%2e' . $ip[2] . '%2e' . $ip[3]; # Full Address
  $groups[] = '@ip%3a' . $ip[0] . '%2e' . $ip[1] . '%2e' . $ip[2]; # Class C
  $groups[] = '@ip%3a' . $ip[0] . '%2e' . $ip[1]; # Class B
  $groups[] = '@ip%3a' . $ip[0]; # Class A
  if ($user){
    //add User
    $groups[] = $user;
  }
  //build regexp
  $regexp   = join('|',$groups);
  // --- End of Patch  ----

Please mail emarks, suggestions etc. to bihler [at] iai [dot] uni [dash] bonn [dot] de.

tips/ipacl.txt · Last modified: 2011-03-21 08:53 by 77.11.93.133