Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add file based group driver (#14529)
It allows you to use /etc/group or a custom file as provider for the Horde group information.
- Loading branch information
1 parent
dda226c
commit cb5bdf2
Showing
2 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
<?php | ||
/** | ||
* Copyright 1999-2016 Horde LLC (http://www.horde.org/) | ||
* | ||
* See the enclosed file COPYING for license information (LGPL). If you | ||
* did not receive this file, see http://www.horde.org/licenses/lgpl21. | ||
* | ||
* @author Jan Schneider <jan@horde.org> | ||
* @author Thomas Jarosch <thomas.jarosch@intra2net.com> | ||
* @category Horde | ||
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 | ||
* @package Group | ||
*/ | ||
|
||
/** | ||
* The Horde_Group_File class provides an implementation of the Horde | ||
* group system for integration with /etc/group or a custom group file. | ||
* | ||
* File format is: | ||
* group_name:encrypted_passwd:GID:user_list | ||
* | ||
* encrypted_passwd is 'x' if shadow is used, user_list is comma separated | ||
* Note: the gid is normally the group name. See 'use_gid' parameter. | ||
* | ||
* @author Jan Schneider <jan@horde.org> | ||
* @author Thomas Jarosch <thomas.jarosch@intra2net.com> | ||
* @category Horde | ||
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 | ||
* @package Group | ||
*/ | ||
class Horde_Group_File extends Horde_Group_Base | ||
{ | ||
/** | ||
* List of groups parsed from the group file. | ||
* | ||
* @var array | ||
*/ | ||
protected $_groups = array(); | ||
|
||
/** | ||
* Constructor. | ||
*/ | ||
public function __construct($params) | ||
{ | ||
/* disable caching for now, otherwise we would need to | ||
monitor mtime changes on the input file and build our | ||
cache signature in _sig() including the mtime. | ||
Caching might even be more overhead for this lightweight backend. | ||
*/ | ||
if (isset($params['cache'])) { | ||
$params['cache'] = new Horde_Support_Stub(); | ||
} | ||
|
||
$use_gid = false; | ||
if (isset($params['use_gid'])) { | ||
$use_gid = $params['use_gid']; | ||
} | ||
|
||
parent::__construct($params); | ||
|
||
$fp = @fopen($params['filename'], 'r'); | ||
if (!$fp) { | ||
throw new Horde_Group_Exception( | ||
'Cannot open ' . $params['filename']); | ||
} | ||
|
||
while (!feof($fp)) { | ||
$line = fgets($fp); | ||
|
||
// file format: group_name:encrypted_passwd:GID:user_list | ||
if (!preg_match('/(.*?):.*?:(\\d+?):(.*)/', $line, $m)) { | ||
continue; | ||
} | ||
|
||
// either use gid from file or group name as id | ||
$id = $use_gid ? $m[2] : $m[1]; | ||
$this->_groups[$id] = array( | ||
'name' => $m[1], | ||
'users' => explode(',', $m[3]) | ||
); | ||
} | ||
fclose($fp); | ||
} | ||
|
||
/** | ||
* Checks if a group exists. | ||
* | ||
* @param mixed $gid A group ID. | ||
* | ||
* @return boolean True if the group exists. | ||
* @throws Horde_Group_Exception | ||
*/ | ||
protected function _exists($gid) | ||
{ | ||
return isset($this->_groups[$gid]); | ||
} | ||
|
||
/** | ||
* Returns a group name. | ||
* | ||
* @param mixed $gid A group ID. | ||
* | ||
* @return string The group's name. | ||
* @throws Horde_Group_Exception | ||
* @throws Horde_Exception_NotFound | ||
*/ | ||
protected function _getName($gid) | ||
{ | ||
if (!$this->_exists($gid)) { | ||
throw new Horde_Exception_NotFound(); | ||
} | ||
return $this->_groups[$gid]['name']; | ||
} | ||
|
||
/** | ||
* Returns all available attributes of a group. | ||
* | ||
* @param mixed $gid A group ID. | ||
* | ||
* @return array The group's date. | ||
* @throws Horde_Group_Exception | ||
* @throws Horde_Exception_NotFound | ||
*/ | ||
protected function _getData($gid) | ||
{ | ||
if (!$this->_exists($gid)) { | ||
throw new Horde_Exception_NotFound(); | ||
} | ||
|
||
return array(); | ||
} | ||
|
||
/** | ||
* Returns a list of all groups a user may see, with IDs as keys and names | ||
* as values. | ||
* | ||
* @return array All existing groups. | ||
* @throws Horde_Group_Exception | ||
*/ | ||
protected function _listAll() | ||
{ | ||
$groups = array(); | ||
foreach ($this->_groups as $id => $group) { | ||
$groups[$id] = $group['name']; | ||
} | ||
return $groups; | ||
} | ||
|
||
/** | ||
* Returns a list of users in a group. | ||
* | ||
* @param mixed $gid A group ID. | ||
* | ||
* @return array List of group users. | ||
* @throws Horde_Group_Exception | ||
* @throws Horde_Exception_NotFound | ||
*/ | ||
protected function _listUsers($gid) | ||
{ | ||
if (!$this->exists($gid)) { | ||
throw new Horde_Exception_NotFound(); | ||
} | ||
return $this->_groups[$gid]['users']; | ||
} | ||
|
||
/** | ||
* Returns a list of groups a user belongs to. | ||
* | ||
* @param string $user A user name. | ||
* | ||
* @return array A list of groups, with IDs as keys and names as values. | ||
* @throws Horde_Group_Exception | ||
*/ | ||
protected function _listGroups($user) | ||
{ | ||
$groups = array(); | ||
foreach ($this->_groups as $id => $group) { | ||
if (in_array($user, $group['users'])) { | ||
$groups[$id] = $group['name']; | ||
} | ||
} | ||
return $groups; | ||
} | ||
|
||
/** | ||
* Searches for group names. | ||
* | ||
* @param string $name A search string. | ||
* | ||
* @return array A list of matching groups, with IDs as keys and names as | ||
* values. | ||
* @throws Horde_Group_Exception | ||
*/ | ||
protected function _search($name) | ||
{ | ||
$groups = array(); | ||
foreach ($this->_groups as $id => $group) { | ||
if (stripos($group['name'], $name) !== false) { | ||
$groups[$id] = $group['name']; | ||
} | ||
} | ||
return $groups; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
<?php | ||
/** | ||
* Copyright 2011-2016 Horde LLC (http://www.horde.org/) | ||
* | ||
* @author Thomas Jarosch <thomas.jarosch@intra2net.com> | ||
* @category Horde | ||
* @package Group | ||
* @subpackage UnitTests | ||
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1 | ||
*/ | ||
class Horde_Group_FileTest extends Horde_Group_TestBase | ||
{ | ||
/** | ||
* Group file to write to | ||
* | ||
* @var string | ||
*/ | ||
protected static $_groupfile = ''; | ||
|
||
public function testExists() | ||
{ | ||
$this->_exists('some_none_existing_id'); | ||
} | ||
|
||
/** | ||
* @depends testExists | ||
*/ | ||
public function testGetName() | ||
{ | ||
$this->_getName(); | ||
} | ||
|
||
/** | ||
* @depends testExists | ||
*/ | ||
public function testListAll() | ||
{ | ||
$this->_listAll(); | ||
} | ||
|
||
/** | ||
* @depends testExists | ||
*/ | ||
public function testSearch() | ||
{ | ||
$this->_search(); | ||
} | ||
|
||
/** | ||
* @depends testExists | ||
*/ | ||
public function testListUsers() | ||
{ | ||
$this->_listUsers(); | ||
} | ||
|
||
/** | ||
* @depends testExists | ||
*/ | ||
public function testListGroups() | ||
{ | ||
$this->_listGroups(); | ||
} | ||
|
||
public function testGroupWithUmlaut() | ||
{ | ||
$filename = Horde::getTempFile('Horde_Group_FileTest'); | ||
|
||
$group_name = "Group with Umläut"; | ||
$user_name = "joe"; | ||
|
||
$fp = fopen($filename, "w"); | ||
fprintf($fp, "$group_name:x:1:$user_name\n"); | ||
fclose($fp); | ||
|
||
$params = array("filename" => $filename); | ||
$group = new Horde_Group_File($params); | ||
|
||
$this->assertTrue($group->exists($group_name)); | ||
$this->assertEquals($group_name, $group->getName($group_name)); | ||
$this->assertEquals(array($user_name), $group->listUsers($group_name)); | ||
} | ||
|
||
public function testGidFromFile() | ||
{ | ||
$params = array("filename" => self::$_groupfile, "use_gid" => true); | ||
self::$group = new Horde_Group_File($params); | ||
self::$groupids = array(1, 2, 3); | ||
|
||
$this->assertTrue(self::$group->exists(self::$groupids[0])); | ||
$this->assertTrue(self::$group->exists(self::$groupids[1])); | ||
$this->assertTrue(self::$group->exists(self::$groupids[2])); | ||
$this->assertFalse(self::$group->exists(4242424)); | ||
|
||
$this->assertEquals("My Other Group", self::$group->getName(self::$groupids[1])); | ||
} | ||
|
||
public static function setUpBeforeClass() | ||
{ | ||
self::$_groupfile = Horde::getTempFile('Horde_Group_FileTest'); | ||
|
||
$fp = fopen(self::$_groupfile, "w"); | ||
fprintf($fp, "My Group:x:1:joe\n"); | ||
fprintf($fp, "My Other Group:x:2:joe,jane\n"); | ||
fprintf($fp, "Not My Group:x:3:jeff,steve\n"); | ||
fclose($fp); | ||
|
||
self::$groupids = array("My Group", "My Other Group", "Not My Group"); | ||
} | ||
|
||
public function setUp() | ||
{ | ||
$params = array("filename" => self::$_groupfile); | ||
self::$group = new Horde_Group_File($params); | ||
self::$groupids = array("My Group", "My Other Group", "Not My Group"); | ||
} | ||
|
||
public static function tearDownAfterClass() | ||
{ | ||
unlink(self::$_groupfile); | ||
} | ||
} |