mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 23:00:56 +01:00
use new Univention UDM Rest Api, instead of univention-directory-manager cli
This commit is contained in:
parent
2327faa67c
commit
26a287b7d9
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* API - accounts Univention LDAP backend
|
||||
* API - Accounts backend for Univention
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @author Ralf Becker <rb@stylite.de>
|
||||
@ -15,33 +15,25 @@ namespace EGroupware\Api\Accounts;
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* Univention LDAP Backend for accounts
|
||||
* Accounts backend for Univention
|
||||
*
|
||||
* This backend is mostly identical to LDAP backend and need to be configured in the same way.
|
||||
*
|
||||
* Only difference is that new users get created via univention-directory-manager CLI program,
|
||||
* to generate necesary Kerberos stuff.
|
||||
* Only difference is that some actions are currently done directly via Univention UDM webservice:
|
||||
* - create new users: to generate necesary Kerberos stuff and all password hashes
|
||||
* - password change: to generate als Samba hashes
|
||||
* - create groups with given gidNumber/sambaRID
|
||||
* - rename / -position users or groups, as this is a remove and re-create
|
||||
* (removing and adding entry under new dn via LDAP fails: "Type or value exists")
|
||||
*
|
||||
* New groups are generated via same CLI, if we have an ID/RID to set.
|
||||
*
|
||||
* Existing users and groups need to be renamed via same CLI, as removing and
|
||||
* adding entry under new dn via LDAP fails (Type or value exists).
|
||||
* Once UDM webservice is out of beta, we could think about replacing LDAP accounts stuff completly.
|
||||
* Possible problems to look out for:
|
||||
* - search with sorting
|
||||
* - caching done on LDAP level
|
||||
* - mail account and addressbook is also affected
|
||||
*/
|
||||
class Univention extends Ldap
|
||||
{
|
||||
/**
|
||||
* Attribute with mail address
|
||||
*/
|
||||
const MAIL_ATTR = 'mailprimaryaddress';
|
||||
|
||||
/**
|
||||
* Name of binary to call
|
||||
*
|
||||
* It is a symlink to /usr/share/univention-directory-manager-tools/directory-manager-cli.
|
||||
* Both directories must be included in open_basedir!
|
||||
*/
|
||||
const DIRECTORY_MANAGER_BIN = '/usr/sbin/univention-directory-manager';
|
||||
|
||||
/**
|
||||
* Saves / adds the data of one account
|
||||
*
|
||||
@ -49,34 +41,23 @@ class Univention extends Ldap
|
||||
*
|
||||
* @param array $data array with account-data
|
||||
* @return int|boolean the account_id or false on error
|
||||
* @throws Univention\UdmException on error
|
||||
*/
|
||||
function save(&$data)
|
||||
{
|
||||
// UCS lowercases email when storing
|
||||
$data['account_email'] = strtolower($data['account_email']);
|
||||
|
||||
if (self::available())
|
||||
{
|
||||
$ssh = null;//'/usr/bin/ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -i /var/lib/egroupware/id_rsa root@10.44.22.194';
|
||||
$config = $this->frontend->config && $this->frontend->config['ldap_context'] ?
|
||||
$this->frontend->config : $GLOBALS['egw_info']['server'];
|
||||
|
||||
$udm = new Univention\Udm($config);
|
||||
|
||||
if ($data['account_type'] !== 'g' && (empty($data['account_id']) || !$this->id2name($data['account_id'])))
|
||||
{
|
||||
// empty names give an error: The property Last/First name is required is not valid
|
||||
if (empty($data['account_firstname'])) $data['account_firstname'] = 'n/a';
|
||||
// empty names give an error: The property lastname is required is not valid
|
||||
if (empty($data['account_lastname'])) $data['account_lastname'] = 'n/a';
|
||||
|
||||
$params = array(
|
||||
'users/user','create',
|
||||
'--binddn', $config['ldap_root_dn'],
|
||||
'--bindpwd', 5=>$config['ldap_root_pw'],
|
||||
'--position', $config['ldap_context'],
|
||||
'--set', 'username='.$data['account_lid'],
|
||||
'--set', 'firstname='.$data['account_firstname'],
|
||||
'--set', 'lastname='.$data['account_lastname'],
|
||||
);
|
||||
|
||||
// we can't create a new user without a password, setting a randowm one for now
|
||||
$matches = null;
|
||||
if (empty($data['account_passwd']) || preg_match('/^{([a-z0-9_]+)}/i', $data['account_passwd'], $matches))
|
||||
@ -91,106 +72,36 @@ class Univention extends Ldap
|
||||
//file_put_contents('/tmp/passwords', "$data[account_lid]\t$data[account_passwd]\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
$params[] = '--set'; $params[] = 'password='.$data['account_passwd'];
|
||||
|
||||
// if account_id is given and bigger then 1000, set it to facilitate migration
|
||||
if (!empty($data['account_id']) && $data['account_id'] >= Ads::MIN_ACCOUNT_ID)
|
||||
if (empty($data['account_id']) || $data['account_id'] < Ads::MIN_ACCOUNT_ID)
|
||||
{
|
||||
$params[] = '--set'; $params[] = 'uidNumber='.(int)$data['account_id'];
|
||||
$params[] = '--set'; $params[] = 'sambaRID='.(int)$data['account_id'];
|
||||
unset($data['account_id']);
|
||||
}
|
||||
|
||||
if (!empty($data['account_email']))
|
||||
{
|
||||
$params[] = '--set'; $params[] = 'mailPrimaryAddress='.$data['account_email'];
|
||||
|
||||
// we need to set mailHomeServer, so mailbox gets created for Dovecot
|
||||
// get_default() does not work for Adminstrator, try acc_id=1 instead
|
||||
// if everything fails try hostname ...
|
||||
try {
|
||||
if (!($account = Api\Mail\Account::get_default(false, false, false)))
|
||||
{
|
||||
$account = Api\Mail\Account::read(1);
|
||||
}
|
||||
$hostname = $account->acc_imap_host;
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
unset($e);
|
||||
}
|
||||
//$hostname='master.test-org.intranet';
|
||||
if (empty($hostname)) $hostname = trim(exec('hostname -f'));
|
||||
$params[] = '--set'; $params[] = 'mailHomeServer='.$hostname;
|
||||
}
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
if (isset($ssh)) $cmd = $ssh.' bash -c "\\"'.$cmd.'\\""';
|
||||
$output_arr = $ret = $matches = null;
|
||||
exec($cmd, $output_arr, $ret);
|
||||
$output = implode("\n", $output_arr);
|
||||
if ($ret || !preg_match('/^Object created: (uid=.*)$/mui', $output, $matches))
|
||||
{
|
||||
$params[5] = '********'; // mask out password!
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output);
|
||||
}
|
||||
$data['account_dn'] = $matches[1];
|
||||
$data['account_dn'] = $udm->createUser($data);
|
||||
$data['account_id'] = $this->name2id($data['account_lid'], 'account_lid', 'u');
|
||||
}
|
||||
// create new groups with given account_id via directory-manager too, to be able to set the RID
|
||||
elseif($data['account_type'] === 'g' && !empty($data['account_id']) &&
|
||||
$data['account_id'] >= Ads::MIN_ACCOUNT_ID && !$this->id2name($data['account_id']))
|
||||
{
|
||||
$params = array(
|
||||
'groups/group', 'create',
|
||||
'--binddn', $config['ldap_root_dn'],
|
||||
'--bindpwd', 5=>$config['ldap_root_pw'],
|
||||
'--position', empty($config['ldap_group_context']) ? $config['ldap_context'] : $config['ldap_group_context'],
|
||||
'--set', 'name='.$data['account_lid'],
|
||||
'--set', 'gidNumber='.(int)$data['account_id'],
|
||||
'--set', 'sambaRID='.(int)$data['account_id'],
|
||||
);
|
||||
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
if (isset($ssh)) $cmd = $ssh.' bash -c "\\"'.$cmd.'\\""';
|
||||
$output_arr = $ret = $matches = null;
|
||||
exec($cmd, $output_arr, $ret);
|
||||
$output = implode("\n", $output_arr);
|
||||
if ($ret || !preg_match('/^Object created: (cn=.*)$/mui', $output, $matches))
|
||||
{
|
||||
$params[5] = '********'; // mask out password!
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output);
|
||||
}
|
||||
$data['account_dn'] = $matches[1];
|
||||
$data['account_dn'] = $udm->createGroup($data);
|
||||
$data['account_id'] = $this->name2id($data['account_lid'], 'account_lid', 'g');
|
||||
}
|
||||
elseif($data['account_id'] && ($data['old_loginid'] || ($data['old_loginid'] = $this->id2name($data['account_id']))) &&
|
||||
$data['account_lid'] != $data['old_loginid'] &&
|
||||
($data['account_dn'] = $this->id2name($data['account_id'], 'account_dn')))
|
||||
{
|
||||
$params = array(
|
||||
$data['account_type'] !== 'g' ? 'users/user' : 'groups/group', 'modify',
|
||||
'--binddn', $config['ldap_root_dn'],
|
||||
'--bindpwd', 5=>$config['ldap_root_pw'],
|
||||
'--dn', $data['account_dn'],
|
||||
'--set', ($data['account_type'] !== 'g' ? 'username' : 'name').'='.$data['account_lid'],
|
||||
);
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
if (isset($ssh)) $cmd = $ssh.' bash -c "\\"'.$cmd.'\\""';
|
||||
$output_arr = $ret = $matches = null;
|
||||
exec($cmd, $output_arr, $ret);
|
||||
$output = implode("\n", $output_arr);
|
||||
if ($ret || !preg_match('/^Object modified: ((uid|cn)=.*)$/mui', $output, $matches))
|
||||
if ($data['account_type'] !== 'g')
|
||||
{
|
||||
$params[5] = '********'; // mask out password!
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output);
|
||||
$data['account_dn'] = $udm->updateUser($data['account_dn'], $data);
|
||||
}
|
||||
$data['account_dn'] = $data['account_type'] !== 'g' ? $matches[1] :
|
||||
// duno why but directory-manager returns old dn for groups ...
|
||||
preg_replace('/^cn=[^,]+,/', 'cn='.$data['account_lid'].',', $data['account_dn']);
|
||||
else
|
||||
{
|
||||
$data['account_dn'] = $udm->updateGroup($data['account_dn'], $data);
|
||||
}
|
||||
}
|
||||
//else error_log(__METHOD__."() ".self::DIRECTORY_MANAGER_BIN." is NOT available!");
|
||||
return parent::save($data);
|
||||
}
|
||||
|
||||
@ -246,96 +157,50 @@ class Univention extends Ldap
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if our function depending on an external binary is available
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function available()
|
||||
{
|
||||
//return true;
|
||||
return file_exists(self::DIRECTORY_MANAGER_BIN) && is_executable(self::DIRECTORY_MANAGER_BIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* changes password in LDAP
|
||||
*
|
||||
* If $old_passwd is given, the password change is done binded as user and NOT with the
|
||||
* "root" dn given in the configurations.
|
||||
* Change password via UDM to update all hashes supported by Univention
|
||||
*
|
||||
* @param string $old_passwd must be cleartext or empty to not to be checked
|
||||
* @param string $new_passwd must be cleartext
|
||||
* @param int $account_id account id of user whose passwd should be changed
|
||||
* @param boolean $update_lastchange =true
|
||||
* @return boolean true if password successful changed, false otherwise
|
||||
* @throws Univention\UdmException on error
|
||||
*/
|
||||
function change_password($old_passwd, $new_passwd, $account_id=0, $update_lastchange=true)
|
||||
{
|
||||
if (!self::available())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!$account_id)
|
||||
{
|
||||
$username = $GLOBALS['egw_info']['user']['account_lid'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$username = Api\Translation::convert($GLOBALS['egw']->accounts->id2name($account_id),
|
||||
Api\Translation::charset(),'utf-8');
|
||||
}
|
||||
if ($this->debug) error_log(__METHOD__."('$old_passwd','$new_passwd',$account_id, $update_lastchange) username='$username'");
|
||||
|
||||
$filter = str_replace(array('%user','%domain'),array($username,$GLOBALS['egw_info']['user']['domain']),
|
||||
$GLOBALS['egw_info']['server']['ldap_search_filter'] ? $GLOBALS['egw_info']['server']['ldap_search_filter'] : '(uid=%user)');
|
||||
|
||||
$ds = $ds_admin = Api\Ldap::factory();
|
||||
$sri = ldap_search($ds, $GLOBALS['egw_info']['server']['ldap_context'], $filter);
|
||||
$allValues = ldap_get_entries($ds, $sri);
|
||||
|
||||
if ($update_lastchange)
|
||||
{
|
||||
// ToDo: $entry['shadowlastchange'] = round((time()-date('Z')) / (24*3600));
|
||||
}
|
||||
|
||||
$dn = $allValues[0]['dn'];
|
||||
$dn = $this->id2name($account_id ? $account_id : $GLOBALS['egw_info']['user']['account_id'], 'account_dn');
|
||||
if ($this->debug) error_log(__METHOD__."('$old_passwd','$new_passwd',$account_id, $update_lastchange) db='$dn'");
|
||||
|
||||
if($old_passwd) // if old password given (not called by admin) --> bind as that user to change the pw
|
||||
{
|
||||
try {
|
||||
$ds = Api\Ldap::factory(true, '', $dn, $old_passwd);
|
||||
Api\Ldap::factory(true, '', $dn, $old_passwd);
|
||||
}
|
||||
catch (Api\Exception\NoPermission $e) {
|
||||
unset($e);
|
||||
return false; // wrong old user password
|
||||
}
|
||||
}
|
||||
$ssh = null;//'/usr/bin/ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -i /var/lib/egroupware/id_rsa root@10.44.22.194';
|
||||
|
||||
$config = $this->frontend->config && $this->frontend->config['ldap_context'] ?
|
||||
$this->frontend->config : $GLOBALS['egw_info']['server'];
|
||||
|
||||
$params = array(
|
||||
'users/user','modify',
|
||||
'--binddn', $config['ldap_root_dn'],
|
||||
'--bindpwd', 5=>$config['ldap_root_pw'],
|
||||
'--dn', $dn,
|
||||
'--set', 'password='.$new_passwd,
|
||||
);
|
||||
$udm = new Univention\Udm($config);
|
||||
|
||||
$data = [
|
||||
'account_passwd' => $new_passwd
|
||||
];
|
||||
if ($old_passwd)
|
||||
{
|
||||
$params[] = '--set';
|
||||
$params[] = 'pwdChangeNextLogin=0';
|
||||
$data['pwdChangeNextLogin'] = false;
|
||||
}
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
if (isset($ssh)) $cmd = $ssh.' bash -c "\\"'.$cmd.'\\""';
|
||||
$output_arr = $ret = $matches = null;
|
||||
exec($cmd, $output_arr, $ret);
|
||||
$output = implode("\n", $output_arr);
|
||||
if ($ret || !preg_match('/^Object modified: ((uid|cn)=.*)$/mui', $output, $matches))
|
||||
if ($update_lastchange)
|
||||
{
|
||||
$params[5] = '********'; // mask out password!
|
||||
$cmd = self::DIRECTORY_MANAGER_BIN.' '.implode(' ', array_map('escapeshellarg', $params));
|
||||
throw new Api\Exception\WrongUserinput($cmd."\nreturned\n".$output);
|
||||
// ToDo: $entry['shadowlastchange'] = round((time()-date('Z')) / (24*3600));
|
||||
}
|
||||
|
||||
$udm->updateUser($dn, $data);
|
||||
|
||||
if($old_passwd) // if old password given (not called by admin) update the password in the session
|
||||
{
|
||||
// using time() is sufficient to represent the current time, we do not need the timestamp written to the storage
|
||||
|
348
api/src/Accounts/Univention/Udm.php
Normal file
348
api/src/Accounts/Univention/Udm.php
Normal file
@ -0,0 +1,348 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware support for Univention UDM REST Api
|
||||
*
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*
|
||||
* @link https://www.univention.com/blog-en/2019/07/udm-rest-api-beta-version-released/
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage accounts
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Accounts\Univention;
|
||||
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* Univention UDM REST Api
|
||||
*
|
||||
* @todo Use just UDM instead of still calling ldap/parent
|
||||
*/
|
||||
class Udm
|
||||
{
|
||||
/**
|
||||
* Config to use
|
||||
*
|
||||
* @var array $config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Hostname of master, derived from ldap_host
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $host;
|
||||
|
||||
/**
|
||||
* Username, derived from ldap_root_dn
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Udm url prefix, prepend to relative path like 'users/user'
|
||||
*/
|
||||
const PREFIX = '/univention/udm/';
|
||||
|
||||
/**
|
||||
* Log webservice-calls to error_log
|
||||
*/
|
||||
const DEBUG = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $config =null config to use, default $GLOBALS['egw_info']['server']
|
||||
* @throws Api\Exception\WrongParameter for missing LDAP config
|
||||
*/
|
||||
public function __construct(array $config=null)
|
||||
{
|
||||
$this->config = isset($config) ? $config : $GLOBALS['egw_info']['server'];
|
||||
|
||||
$this->host = parse_url($this->config['ldap_host'], PHP_URL_HOST);
|
||||
if (empty($this->host))
|
||||
{
|
||||
throw new Api\Exception\WrongParameter ("Univention needs 'ldap_host' configured!");
|
||||
}
|
||||
$matches = null;
|
||||
if (!preg_match('/^(cn|uid)=([^,]+),/i', $this->config['ldap_root_dn'], $matches))
|
||||
{
|
||||
throw new Api\Exception\WrongParameter ("Univention needs 'ldap_rood_dn' configured!");
|
||||
}
|
||||
$this->user = $matches[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Call UDM REST Api
|
||||
*
|
||||
* @param string $_path path to call, if relative PREFIX is prepended eg. 'users/user'
|
||||
* @param string $_method ='GET'
|
||||
* @param array $_payload =[] payload to send
|
||||
* @param array& $headers =[] on return response headers
|
||||
* @param string $if_match =null etag for If-Match header
|
||||
* @param boolean $return_dn =false return DN of Location header
|
||||
* @throws Exception on error
|
||||
* @return array|string decoded JSON or DN for $return_DN === true
|
||||
* @throws UdmCantConnect for connection errors or JSON decoding errors
|
||||
* @throws UdmError for returned JSON error object
|
||||
* @throws UdmMissingLocation for missing Location header with DN ($return_dn === true)
|
||||
*/
|
||||
protected function call($_path, $_method='GET', array $_payload=[], &$headers=[], $if_match=null, $return_dn=false)
|
||||
{
|
||||
$curl = curl_init();
|
||||
|
||||
// fix error: Request argument "policies" is not a "dict" (PHP encodes empty arrays as array, not object)
|
||||
/*if (array_key_exists('policies', $_payload) && empty($_payload['policies']))
|
||||
{
|
||||
$_payload['policies'] = new \stdClass(); // force "policies": {}
|
||||
}*/
|
||||
|
||||
$headers = [];
|
||||
$curlOpts = [
|
||||
CURLOPT_URL => 'https://'.$this->host.($_path[0] !== '/' ? self::PREFIX : '').$_path,
|
||||
CURLOPT_USERPWD => $this->user.':'.$this->config['ldap_root_pw'],
|
||||
//CURLOPT_SSL_VERIFYHOST => 2, // 0: to disable certificate check
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
],
|
||||
CURLOPT_CUSTOMREQUEST => $_method,
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
//CURLOPT_FOLLOWLOCATION => 1,
|
||||
CURLOPT_TIMEOUT => 1,
|
||||
CURLOPT_VERBOSE => 1,
|
||||
CURLOPT_HEADERFUNCTION =>
|
||||
function($curl, $header) use (&$headers)
|
||||
{
|
||||
$len = strlen($header);
|
||||
$header = explode(':', $header, 2);
|
||||
if (count($header) < 2)
|
||||
{
|
||||
$headers[] = $header[0]; // http status
|
||||
return $len;
|
||||
}
|
||||
$name = strtolower(trim($header[0]));
|
||||
if (!array_key_exists($name, $headers))
|
||||
{
|
||||
$headers[$name] = trim($header[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$headers[$name] = [$headers[$name]];
|
||||
$headers[$name][] = trim($header[1]);
|
||||
}
|
||||
unset($curl); // not used, but required by function signature
|
||||
return $len;
|
||||
},
|
||||
];
|
||||
if (isset($if_match))
|
||||
{
|
||||
$curlOpts[CURLOPT_HTTPHEADER][] = 'If-Match: '.$if_match;
|
||||
}
|
||||
switch($_method)
|
||||
{
|
||||
case 'PUT':
|
||||
case 'POST':
|
||||
$curlOpts[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json';
|
||||
$curlOpts[CURLOPT_POSTFIELDS] = json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
|
||||
break;
|
||||
|
||||
case 'GET':
|
||||
default:
|
||||
if ($_payload)
|
||||
{
|
||||
$curlOpts[CURLOPT_URL] .= '?'. http_build_query($_payload);
|
||||
}
|
||||
break;
|
||||
}
|
||||
curl_setopt_array($curl, $curlOpts);
|
||||
$response = curl_exec($curl);
|
||||
|
||||
if (!$response || !($json = json_decode($response, true)) && json_last_error())
|
||||
{
|
||||
$info = curl_getinfo($curl);
|
||||
curl_close($curl);
|
||||
error_log(__METHOD__."($_path, $_method, ".json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).") returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).", curl_getinfo()=".json_encode($info, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
throw new UdmCantConnect("Error contacting Univention UDM REST Api ($_path)".($response ? ': '.json_last_error() : ''));
|
||||
}
|
||||
curl_close($curl);
|
||||
if (!empty($json['error']))
|
||||
{
|
||||
error_log(__METHOD__."($_path, $_method, ".json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).") returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
throw new UdmError("UDM REST Api ($_path): ".(empty($json['error']['message']) ? $response : $json['error']['message']), $json['error']['code']);
|
||||
}
|
||||
if (self::DEBUG) error_log(__METHOD__."($_path, $_method, ".json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).") returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
|
||||
if ($return_dn)
|
||||
{
|
||||
$matches = null;
|
||||
if (!isset($headers['location']) || !preg_match('|/([^/]+)$|', $headers['location'], $matches))
|
||||
{
|
||||
throw new UdmMissingLocation("UDM REST Api ($_path) did not return Location header!");
|
||||
}
|
||||
return urldecode($matches[1]);
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user
|
||||
*
|
||||
* @param array $data
|
||||
* @throws Exception on error-message
|
||||
* @return string with DN of new user
|
||||
*/
|
||||
public function createUser(array $data)
|
||||
{
|
||||
// set default values
|
||||
$payload = $this->user2udm($data, $this->call('users/user/add')['entry']);
|
||||
|
||||
$payload['superordinate'] = null;
|
||||
$payload['position'] = $this->config['ldap_context'];
|
||||
|
||||
$headers = [];
|
||||
return $this->call('users/user/', 'POST', $payload, $headers, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user
|
||||
*
|
||||
* @param string $dn dn of user to update
|
||||
* @param array $data
|
||||
* @return string with dn
|
||||
* @throws Exception on error-message
|
||||
*/
|
||||
public function updateUser($dn, array $data)
|
||||
{
|
||||
// set existing values
|
||||
$get_headers = [];
|
||||
$payload = $this->user2udm($data, $this->call('users/user/'.urlencode($dn), 'GET', [], $get_headers)['entry']);
|
||||
|
||||
$headers = [];
|
||||
return $this->call('users/user/', 'PUT', $payload, $headers, $get_headers['etag'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy EGroupware user-values to UDM ones
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $payload
|
||||
* @return array with updated payload
|
||||
*/
|
||||
protected function user2udm(array $data, array $payload)
|
||||
{
|
||||
foreach([
|
||||
'account_lid' => 'username',
|
||||
'account_passwd' => 'password',
|
||||
'account_lastname' => 'lastname',
|
||||
'account_firstname' => 'firstname',
|
||||
'account_id' => ['uidNumber', 'sambaRID'],
|
||||
'account_email' => 'mailPrimaryAddress',
|
||||
] as $egw => $names)
|
||||
{
|
||||
if (!empty($data[$egw]))
|
||||
{
|
||||
foreach((array)$names as $name)
|
||||
{
|
||||
if (!array_key_exists($name, $payload['properties']))
|
||||
{
|
||||
throw new \Exception ("No '$name' in properties: ".json_encode($payload['properties']));
|
||||
}
|
||||
$payload['properties'][$name] = $data[$egw];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($data['account_email']))
|
||||
{
|
||||
// we need to set mailHomeServer, so mailbox gets created for Dovecot
|
||||
// get_default() does not work for Adminstrator, try acc_id=1 instead
|
||||
// if everything fails try ldap host / master ...
|
||||
try {
|
||||
if (!($account = Api\Mail\Account::get_default(false, false, false)))
|
||||
{
|
||||
$account = Api\Mail\Account::read(1);
|
||||
}
|
||||
$hostname = $account->acc_imap_host;
|
||||
}
|
||||
catch(\Exception $e) {
|
||||
unset($e);
|
||||
}
|
||||
if (empty($hostname)) $hostname = $this->host;
|
||||
$payload['properties']['mailHomeServer'] = $hostname;
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a group
|
||||
*
|
||||
* @param array $data
|
||||
* @throws Exception on error-message
|
||||
* @return string with DN of new user
|
||||
*/
|
||||
public function createGroup(array $data)
|
||||
{
|
||||
// set default values
|
||||
$payload = $this->group2udm($data, $this->call('groups/group/add')['entry']);
|
||||
|
||||
$payload['superordinate'] = null;
|
||||
$payload['position'] = empty($this->config['ldap_group_context']) ? $this->config['ldap_context'] : $this->config['ldap_group_context'];
|
||||
|
||||
$headers = [];
|
||||
return $this->call('groups/group/', 'POST', $payload, $headers, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a group
|
||||
*
|
||||
* @param string $dn dn of group to update
|
||||
* @param array $data
|
||||
* @throws Exception on error-message
|
||||
* @return string with DN of new user
|
||||
*/
|
||||
public function updateGroup($dn, array $data)
|
||||
{
|
||||
// set existing values
|
||||
$get_headers = [];
|
||||
$payload = $this->user2udm($data, $this->call('groups/group/'.urlencode($dn), 'GET', [], $get_headers)['entry']);
|
||||
|
||||
$headers = [];
|
||||
return $this->call('groups/group/', 'PUT', $payload, $headers, $get_headers['etag'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy EGroupware group values to UDM ones
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $payload
|
||||
* @return array with updated payload
|
||||
*/
|
||||
protected function group2udm(array $data, array $payload)
|
||||
{
|
||||
foreach([
|
||||
'account_lid' => 'name',
|
||||
'account_id' => ['gidNumber', 'sambaRID'],
|
||||
] as $egw => $names)
|
||||
{
|
||||
if (!empty($data[$egw]))
|
||||
{
|
||||
foreach((array)$names as $name)
|
||||
{
|
||||
if (!array_key_exists($name, $payload['properties']))
|
||||
{
|
||||
throw new \Exception ("No '$name' in properties: ".json_encode($payload['properties']));
|
||||
}
|
||||
$payload['properties'][$name] = $data[$egw];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
}
|
26
api/src/Accounts/Univention/UdmCantConnect
Normal file
26
api/src/Accounts/Univention/UdmCantConnect
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware support for Univention UDM REST Api
|
||||
*
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*
|
||||
* @link https://www.univention.com/blog-en/2019/07/udm-rest-api-beta-version-released/
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage accounts
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Accounts\Univention;
|
||||
|
||||
/**
|
||||
* Cant connect to UDM Rest API or no JSON returned
|
||||
*/
|
||||
class UdmCantConnect extends UdmException
|
||||
{
|
||||
public function __construct($msg = null, $code = 100, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($msg, $code, $previous);
|
||||
}
|
||||
}
|
26
api/src/Accounts/Univention/UdmError.php
Normal file
26
api/src/Accounts/Univention/UdmError.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware support for Univention UDM REST Api
|
||||
*
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*
|
||||
* @link https://www.univention.com/blog-en/2019/07/udm-rest-api-beta-version-released/
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage accounts
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Accounts\Univention;
|
||||
|
||||
/**
|
||||
* UDM Rest API returned a JSON error object
|
||||
*/
|
||||
class UdmError extends UdmException
|
||||
{
|
||||
public function __construct($msg = null, $code = 100, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($msg, $code, $previous);
|
||||
}
|
||||
}
|
28
api/src/Accounts/Univention/UdmException.php
Normal file
28
api/src/Accounts/Univention/UdmException.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware support for Univention UDM REST Api
|
||||
*
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*
|
||||
* @link https://www.univention.com/blog-en/2019/07/udm-rest-api-beta-version-released/
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage accounts
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Accounts\Univention;
|
||||
|
||||
use EGroupware\Api;
|
||||
|
||||
/**
|
||||
* UDM Rest API base for all exceptions thrown from Udm (exception WrongParameter from constructor for missing LDAP config)
|
||||
*/
|
||||
class UdmException extends Api\Exception
|
||||
{
|
||||
public function __construct($msg = null, $code = 100, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($msg, $code, $previous);
|
||||
}
|
||||
}
|
26
api/src/Accounts/Univention/UdmMissingLocation.php
Normal file
26
api/src/Accounts/Univention/UdmMissingLocation.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware support for Univention UDM REST Api
|
||||
*
|
||||
* @link https://www.egroupware.org
|
||||
* @author Ralf Becker <rb@egroupware.org>
|
||||
*
|
||||
* @link https://www.univention.com/blog-en/2019/07/udm-rest-api-beta-version-released/
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @subpackage accounts
|
||||
*/
|
||||
|
||||
namespace EGroupware\Api\Accounts\Univention;
|
||||
|
||||
/**
|
||||
* UDM Rest API returned no Location header
|
||||
*/
|
||||
class UdmMissingLocation extends UdmException
|
||||
{
|
||||
public function __construct($msg = null, $code = 100, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($msg, $code, $previous);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user