<?php
/**
* iF.SVNAdmin
* Copyright (c) 2010 by Manuel Freiholz
* http://www.insanefactory.com/
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; version 2
* of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.
*/
/**
* This class provides functionality to manage the SVNAuthFile.
* Managing group and user rights for repositories.
*
* <p><b>Note:</b> This class does not manage user Accounts.
*
* @author Manuel Freiholz (Gainwar)
* @since 08/02/2009
* @copyright insaneFactory.com
* @require inifile.func.php
*/
class IF_SVNAuthFileC
{
public $GROUP_SIGN = "@";
public $GROUP_SECTION = "groups";
public $ALIAS_SECTION = "alias";
public static $PERMISSION_NONE = "";
public static $PERMISSION_READ = "r";
public static $PERMISSION_READWRITE = "rw";
private $m_authfile = NULL;
private $m_errno = 0;
public $m_data = array();
/**
*
* @return unknown_type
*/
public function __construct()
{
}
/**
* Returns the error number, if any error occured.
*
* @return int
*/
public function errno()
{
return $this->m_errno;
}
/**
* Gets the error definition string according to the error number.
*
* @return string
*/
public function error()
{
switch($this->m_errno)
{
case 100: return "The file does not exists.";
case 101: return "No permission to read the file content.";
case 102: return "Could not read file contents from file.";
case 110: return "Error during parsing file.";
case 120: return "The repository path is already configured.";
case 121: return "Invalid access path syntax. (Right: <reponame>:/<accesspath>)";
case 123: return "The repository path is not configured.";
case 130: return "Could not save file to disk.";
case 140: return "No groups section defined.";
case 141: return "The group already exists.";
case 142: return "The group does not exist.";
case 143: return "Invalid group name. (A-Z, 0-9, -, _)";
case 144: return "The user is already in group.";
case 145: return "The user is not in group.";
case 0: return "No error.";
case 1: return "Unknown error.";
default: return "Unknown error";
}
return NULL;
}
/**
* open the given SVNAuthFile, which contains permissions
* of the svn users/groups.
*
* @param $authfile Path to the SVNAuthFile
* @return bool
*/
public function open( $authfile )
{
$this->m_authfile = $authfile;
if( !file_exists($authfile) )
{
$this->m_errno = 100;
return false;
}
if( !is_readable($authfile) )
{
$this->m_errno = 101;
return false;
}
// load file contents into the local member variable.
//$fileContent = file_get_contents($authfile);
//if( $fileContent == false )
//{
// $this->m_errno = 102;
// return false;
//}
// parse file.
$this->m_data = if_parse_ini_file($authfile);
if( $this->m_data == false )
{
//$this->m_errno = 110;
//return false;
}
// everything works OK.
return true;
}
/**
* Writes the changed authfile to the given destination file. If $dest is
* NULL then it will be written to the same file from which the data has
* been read.
*
* @param string $dest
* @return bool
*/
public function save( $dest = NULL )
{
if( $dest == NULL )
{
$dest = $this->m_authfile;
}
// write data to file.
$flag = if_write_ini_file( $dest, $this->m_data );
if( !$flag )
{
$this->m_errno = 130;
return false;
}
return true;
}
/**
* Gets all configured groups.
*
* @return array Filled with group names or FALSE if the section "groups" doesn't exists.
*/
public function &groups()
{
$ret = array();
// assoc array "groupname=>users".
$groups =& $this->m_data[$this->GROUP_SECTION];
if( is_array($groups) )
{
foreach( $groups as $k=>&$v )
{
array_push($ret, $k);
}
}
else
{
// the section does not exists.
$this->m_errno = 140;
return $ret;
}
return $ret;
}
/**
* Gets all configured repositories.
*
* @return array<string>
*/
public function &repositories()
{
$ret = array();
// assoc array "section=>key=>value".
// get all section names, but not the section with name "groups".
foreach( $this->m_data as $sec=>&$v )
{
if( $sec != $this->GROUP_SECTION && $sec != $this->ALIAS_SECTION )
{
array_push($ret, $sec);
}
}
return $ret;
}
/**
* Gets all users of the given group.
*
* @param string $group
* @return array with string's of users or FALSE on error.
*/
public function &usersOfGroup( &$group )
{
$ret = array();
// get group section array.
$groups =& $this->m_data[$this->GROUP_SECTION];
if( !is_array($groups) )
{
// the group section does not exist.
$this->m_errno = 140;
return false;
}
// search for group.
$users = NULL;
foreach( $groups as $grpname=>&$v )
{
if( $grpname == $group )
{
$users =& $v;
break;
}
}
if( !empty($users) )
{
$ret = explode(",",$users);
for( $i=0; $i<count($ret); $i++ )
{
$ret[$i] = trim($ret[$i]);
}
}
return $ret;
}
/**
* Gets all users which have direct rights to this repository path.
*
* @param $repo
* @return array with string's of users.
*/
public function &usersOfRepository( &$repo )
{
$ret = array();
// get the keys of the wished section.
$keys =& $this->m_data[$repo];
if( is_array($keys) )
{
// iterate the array and collect all users. (skip groups)
// $k = username
// $v = permission
foreach( $keys as $k=>&$v )
{
$pos = strpos($k,$this->GROUP_SIGN);
if( $pos === false )
{
$data = array();
$data[0] = $k; // pass by value
$data[1] = $v; // pass by value
// its a user.
array_push($ret,$data);
}
else
{
// its a group.
continue;
}
}
}
return $ret;
}
/**
* Gets all groups which have direct rights to this repository path.
*
* @param $repo
* @return array with string's of groups.
*/
public function &groupsOfRepository( &$repo )
{
$ret = array();
// get the keys of the wished section.
$keys =& $this->m_data[$repo];
if( is_array($keys) )
{
// iterate the array and collect all groups. (skip users)
foreach( $keys as $k=>&$v )
{
$pos = strpos($k,$this->GROUP_SIGN);
if( $pos === false )
{
// its a user.
continue;
}
else
{
$data = array();
$data[0] = substr($k,1);
$data[1] = $v;
// its a group.
array_push($ret,$data);
}
}
}
return $ret;
}
/**
* Gets all groups of which the user is a member.
*
* @param $username
* @return unknown_type
*/
public function &groupsOfUser( &$username )
{
$groups =& $this->groups();
$grpList = array();
if( !is_array( $groups ) )
{
return $grpList;
}
// Iterate all groups and check whether the user is a member of it.
foreach( $groups as &$g )
{
$users =& $this->usersOfGroup( $g );
if( in_array( $username, $users ) )
{
array_push( $grpList, $g );
}
}
return $grpList;
}
/**
* Gets all repository paths which have the group associated.
*
* @param string $groupname
* @return array<string> List of repository paths
*/
public function &repositoryPathsOfGroup( &$groupname )
{
$grpname = $this->GROUP_SIGN.$groupname;
// List of repo paths.
$list = array();
// Iterate all repositories and search for the groupname.
foreach( $this->m_data as $sec=>&$v )
{
// Iterate all groups and users of the current repository path iteration.
foreach( $v as $name=>&$notneeded )
{
// Check whether the current $name is the groupname.
if( $name == $grpname )
{
// Yes it is, add the repository path to the return list and abort searching for more inside of this repo.
array_push( $list, $sec );
break;
}
}
}
return $list;
}
/**
* Gets all repository paths which have the user associated.
*
* @param string $username
* @return array<string> List of repository paths
*/
public function &repositoryPathsOfUser( &$username )
{
// List of repo paths.
$list = array();
// Iterate all repositories and search for the username.
foreach( $this->m_data as $sec=>&$v )
{
// Iterate all groups and users of the current repository path iteration.
foreach( $v as $name=>&$notneeded )
{
// Check whether the current $name is the groupname.
if( $name == $username )
{
// Yes it is, add the repository path to the return list and abort searching for more inside of this repo.
array_push( $list, $sec );
break;
}
}
}
return $list;
}
/**
* Checks whether the repository path already exists in the configuration.
*
* @param string $repopath the repository path
* @return bool true/false
*/
public function repositoryPathExists( &$repopath )
{
// assoc array "section=>key=>value".
// get all section names, but not the section with name "groups".
foreach( $this->m_data as $sec=>&$v )
{
if( $sec == $repopath )
{
return true;
}
}
return false;
}
/**
* Adds a new repostory configuration path to the SVNAuthFile.
*
* @param string $repopath
* @return bool true/false
*/
public function addRepositoryPath( &$repopath )
{
if( self::repositoryPathExists( $repopath ) )
{
$this->m_errno = 120;
return false;
}
// Validate the $repopath string.
$pattern = '/^[A-Za-z0-9\_\-]+:\/.*$/i';
if( $repopath != "/" && !preg_match( $pattern, $repopath ) )
{
$this->m_errno = 121;
return false;
}
// Create the repository configuration path.
$this->m_data[$repopath] = array();
return true;
}
/**
* Removes the access path from the configuration.
* @param $repopath
* @return unknown_type
*/
public function removeRepositoryPath( &$repopath )
{
if( !self::repositoryPathExists( $repopath ) )
{
$this->m_errno = 123; // The repository path does not exist.
return false;
}
// Remove item from array.
unset( $this->m_data[$repopath] );
return true;
}
/**
* Checks whether the group "$groupname" already exists.
* @param $groupname
* @return bool
*/
public function groupExists( &$groupname )
{
// Get the groups array.
$groups =& $this->m_data[$this->GROUP_SECTION];
if( !is_array($groups) )
{
// No groups defined.
$this->m_errno = 140;
return false;
}
if( isset( $groups[$groupname] ) )
{
return true;
}
return false;
}
/**
* Creates the new group "$groupname", if it does not exist.
* @param string $groupname
* @return bool TRUE/FALSE
*/
public function createGroup( &$groupname )
{
// Validate the groupname.
$pattern = '/^[A-Za-z0-9\-\_]+$/i';
if( !preg_match( $pattern, $groupname ) )
{
$this->m_errno = 143;
return false;
}
if( self::groupExists( $groupname ) )
{
$this->m_errno = 141;
return false;
}
// Get the groups array and add the new group to it.
$this->m_data[$this->GROUP_SECTION][$groupname] = "";
return true;
}
/**
* Deletes the given group by name.
* @param $groupname
* @return bool
*/
public function deleteGroup( &$groupname )
{
if( !self::groupExists( $groupname ) )
{
$this->m_errno = 142;
return false;
}
// Delete the group now.
unset( $this->m_data[$this->GROUP_SECTION][$groupname] );
return true;
}
/**
* Adds the user to group.
* @param $groupname
* @param $username
* @return unknown_type
*/
public function addUserToGroup( &$groupname, &$username )
{
if( !self::groupExists( $groupname ) )
{
$this->m_errno = 142;
return false;
}
// Get current users.
$users =& $this->usersOfGroup( $groupname );
if( !is_array($users) )
{
return false;
}
// NOTE: Its no longer an error when the user is already in group!!!
// Check whether the user is already in group.
if( in_array( $username, $users ) )
{
//$this->m_errno = 144;
//return false;
return true;
}
// Add user to $users array.
array_push( $users, $username );
// Set the array as new value of group.
$this->m_data[$this->GROUP_SECTION][$groupname] = join(",", $users);
return true;
}
/**
* Checks whether the user is in the given group.
*
* @param $groupname
* @param $username
* @return unknown_type
*/
public function isUserInGroup( &$groupname, &$username )
{
$users =& $this->usersOfGroup( $groupname );
if( !is_array( $users ) )
{
return false;
}
foreach( $users as &$usr )
{
if( $usr == $username )
{
return true;
}
}
return false;
}
/**
* Removes the given user from group.
*
* @param $username
* @param $groupname
* @return bool
*/
public function removeUserFromGroup( &$username, &$groupname )
{
$groupUsers =& $this->usersOfGroup( $groupname );
if( !is_array( $groupUsers ) )
{
return false;
}
// Search the user in array.
$pos = array_search( $username, $groupUsers );
if( $pos !== FALSE )
{
// Remove the user from array.
unset( $groupUsers[$pos] );
$userString = join( ",", $groupUsers );
$this->m_data[$this->GROUP_SECTION][$groupname] = $userString;
}
else
{
// User is not in group.
$this->m_errno = 145;
return true;
}
return true;
}
/**
* Removes the given $groupname from $repository.
* @param $groupname string
* @param $repository string
* @return bool
*/
public function removeGroupFromRepository( &$groupname, &$repository )
{
// Does the repo config exists?
if( !isset( $this->m_data[$repository] ) )
{
$this->m_errno = 123;
return false;
}
// The repository section array.
$sec =& $this->m_data[$repository];
// k = user or group name
// v = the assigned right
foreach( $sec as $k=>&$v )
{
// Find out whether the current key is a groupname.
$pos = strpos( $k, $this->GROUP_SIGN );
if( $pos !== FALSE )
{
$grp = substr( $k, 1 );
if( $grp == $groupname )
{
// Remove group from repository.
unset( $this->m_data[$repository][$k] );
return TRUE;
}
}
}
return TRUE;
}
/**
* Removes the given $groupname from $repository.
* @param $groupname string
* @param $repository string
* @return bool
*/
public function removeUserFromRepository( &$username, &$repository )
{
// Does the repo config exists?
if( !isset( $this->m_data[$repository] ) )
{
$this->m_errno = 123;
return false;
}
// The repository section array.
$sec =& $this->m_data[$repository];
// k = user or group name
// v = the assigned right
foreach( $sec as $k=>&$v )
{
// Find out whether the current key is a username.
$pos = strpos( $k, $this->GROUP_SIGN );
if( $pos === FALSE )
{
$usr = &$k;
if( $usr == $username )
{
// Remove user from repository.
unset( $this->m_data[$repository][$k] );
return TRUE;
}
}
}
return TRUE;
}
public function isUserAssignedToRepository( &$username, &$repository, &$permission = NULL )
{
// Does the repo exists?
if( !isset( $this->m_data[$repository] ) )
{
$this->m_errno = 123;
return false;
}
// The repository section array.
$sec =& $this->m_data[$repository];
// k = user or group name
// v = the assigned right
foreach( $sec as $k=>&$v )
{
if( $k == $username )
{
if( $permission == NULL )
{
return true;
}
else
{
if( $v == $permission )
{
return true;
}
else
{
return false;
}
}
}
}
}
public function addUserToRepository( &$username, &$repository, &$permission )
{
// Does the repo exists?
if( !isset( $this->m_data[$repository] ) )
{
$this->m_errno = 123;
return false;
}
// The repository section array.
$sec =& $this->m_data[$repository];
$sec[$username] = $permission;
return true;
}
public function addGroupToRepository( &$groupname, &$repository, &$permission )
{
// Does the repo exists?
if( !isset( $this->m_data[$repository] ) )
{
$this->m_errno = 123;
return false;
}
// The repository section array.
$sec =& $this->m_data[$repository];
$sec[$this->GROUP_SIGN.$groupname] = $permission;
return true;
}
public function &permissionsOfUser( $username )
{
$ret = array();
// Iterate all access paths.
foreach( $this->m_data as $repo_name=>&$v )
{
if( $repo_name == $this->GROUP_SECTION )
{ continue; }
// Iterate the user/group names of the current repository.
foreach( $v as $usergroup_name=>&$val )
{
// User or group name?
if( substr( $usergroup_name, 0, 1 ) == $this->GROUP_SIGN ) // $usergroup_name is a group name.
{
$grpname = substr( $usergroup_name, 1 );
if( self::isUserInGroup($grpname, $username) )
{
$data = array();
$data[0] = $repo_name; // access-path
$data[1] = $val; // permission
$data[2] = $grpname; // group name from which the user owns the rights.
array_push( $ret, $data );
}
}
elseif( $usergroup_name == '*' && $username != '*' )
{
$data = array();
$data[0] = $repo_name;
$data[1] = $val;
$data[2] = '*';
array_push( $ret, $data );
}
else // $usergroup_name is a user name.
{
$usrname = $usergroup_name;
if( $usrname == $username )
{
$data = array();
$data[0] = $repo_name;
$data[1] = $val;
$data[2] = ''; // empty string, cause its a direct permission.
array_push( $ret, $data );
}
}
}
}
return $ret;
}
public function &permissionsOfGroup( $groupname )
{
$ret = array();
$internal_groupname = $this->GROUP_SIGN . $groupname;
// Iterate all repos and search for the given groupname.
foreach( $this->m_data as $repo_name=>&$v )
{
if( $repo_name == $this->GROUP_SECTION )
{
continue;
}
// Iterate the user/group names of the current repository.
foreach( $v as $group_name=>&$val )
{
if( $group_name == $internal_groupname )
{
$data = array();
$data[0] = $repo_name;
$data[1] = $val;
array_push( $ret, $data );
}
}
}//foreach
return $ret;
}
/**
* Gets all rights recursive up including groups, which have
* access to the given repository $path.
*
* Returns an array of users with associated inheritance. (from group or * user.)
*
* @param $path string
* @return array<$username => array<"from" => "grpname", "perm" => "r">>
*/
function getAssignmentsOfPath($path)
{
// TODO: Implement getAssignmentsOfPath()
}
}
?>