forked from extern/egroupware
fix for stable Univention 4.4-2 REST API
This commit is contained in:
parent
6f799d7cb7
commit
5dcf1e842f
@ -557,10 +557,19 @@ class Ldap
|
||||
{
|
||||
$to_write['gidnumber'] = abs($data['account_id']);
|
||||
$to_write['cn'] = $data['account_lid'];
|
||||
if (!empty($data['account_description']) || $old)
|
||||
// do not overwrite exitsting description, if non is given
|
||||
if (isset($data['account_description']))
|
||||
{
|
||||
$to_write['description'] = !empty($data['account_description']) ? $data['account_description'] : array();
|
||||
}
|
||||
// to kope with various dependencies / requirements of objectclasses, simply write everything again
|
||||
foreach($old as $name => $value)
|
||||
{
|
||||
if (!isset($to_write[$name]) && !in_array($name, ['dn', 'objectclass']))
|
||||
{
|
||||
$to_write[$name] = $value;
|
||||
}
|
||||
}
|
||||
return $to_write;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,27 @@ use EGroupware\Api;
|
||||
*/
|
||||
class Univention extends Ldap
|
||||
{
|
||||
/**
|
||||
* Name of mail attribute
|
||||
*/
|
||||
const MAIL_ATTR = 'mailprimaryaddress';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Api\Accounts $frontend reference to the frontend class, to be able to call it's methods if needed
|
||||
*/
|
||||
function __construct(Api\Accounts $frontend)
|
||||
{
|
||||
parent::__construct($frontend);
|
||||
|
||||
// remove not supported groupOfNames to skip parent::save() first tries with it, before trying without
|
||||
if (($groupOfNameKey = array_search('groupofnames', $this->requiredObjectClasses['group'])))
|
||||
{
|
||||
unset($this->requiredObjectClasses['group'][$groupOfNameKey]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves / adds the data of one account
|
||||
*
|
||||
@ -82,9 +103,9 @@ class Univention extends Ldap
|
||||
$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']))
|
||||
// create new groups incl. Samba objectclass and SID
|
||||
elseif($data['account_type'] === 'g' && (empty($data['account_id']) ||
|
||||
$data['account_id'] >= Ads::MIN_ACCOUNT_ID && !$this->id2name($data['account_id'])))
|
||||
{
|
||||
$data['account_dn'] = $udm->createGroup($data);
|
||||
$data['account_id'] = $this->name2id($data['account_lid'], 'account_lid', 'g');
|
||||
|
@ -52,7 +52,7 @@ class Udm
|
||||
/**
|
||||
* Log webservice-calls to error_log
|
||||
*/
|
||||
const DEBUG = true;
|
||||
const DEBUG = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -96,13 +96,16 @@ class Udm
|
||||
{
|
||||
$curl = curl_init();
|
||||
|
||||
// fix error: Request argument "policies" is not a "dict" (PHP encodes empty arrays as array, not object)
|
||||
// fix error like: 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": {}
|
||||
}
|
||||
if (is_array($_payload['properties']) && array_key_exists('umcProperty', $_payload['properties']) && empty($_payload['properties']['umcProperty']))
|
||||
{
|
||||
$_payload['properties']['umcProperty'] = new \stdClass(); // force "umcProperty": {}
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
$curlOpts = [
|
||||
CURLOPT_URL => 'https://'.$this->host.($_path[0] !== '/' ? self::PREFIX : '').$_path,
|
||||
CURLOPT_USERPWD => $this->user.':'.$this->config['ldap_root_pw'],
|
||||
@ -115,29 +118,7 @@ class Udm
|
||||
//CURLOPT_FOLLOWLOCATION => 1,
|
||||
CURLOPT_TIMEOUT => 30, // setting a timeout of 30 seconds, as recommended by Univention
|
||||
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;
|
||||
},
|
||||
CURLOPT_HEADER => 1,
|
||||
];
|
||||
if (isset($if_match))
|
||||
{
|
||||
@ -162,27 +143,32 @@ class Udm
|
||||
curl_setopt_array($curl, $curlOpts);
|
||||
$response = curl_exec($curl);
|
||||
|
||||
$header_size = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
|
||||
$headers = self::getHeaders(substr($response, 0, $header_size));
|
||||
$body = substr($response, $header_size);
|
||||
|
||||
$path = urldecode($_path); // for nicer error-messages
|
||||
if (!$response || !($json = json_decode($response, true)) && json_last_error())
|
||||
if ($response === false || $body !== '' && !($json = json_decode($body, true)) && json_last_error())
|
||||
{
|
||||
$info = curl_getinfo($curl);
|
||||
curl_close($curl);
|
||||
if ($retry > 0)
|
||||
{
|
||||
error_log(__METHOD__."($path, $_method, ...) failed, retrying in 100ms, returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).", curl_getinfo()=".json_encode($info, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
error_log(__METHOD__."($path, $_method, ...) failed, retrying in 100ms, returned $body, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).", curl_getinfo()=".json_encode($info, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
usleep(100000);
|
||||
return $this->call($_path, $_method, $_payload, $headers, $if_match, $return_dn, --$retry);
|
||||
}
|
||||
error_log(__METHOD__."($path, $_method, ...) returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).", curl_getinfo()=".json_encode($info, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
error_log(__METHOD__."($path, $_method, ...) returned $body, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).", curl_getinfo()=".json_encode($info, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
error_log(__METHOD__."($path, $_method, ".json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).")");
|
||||
throw new UdmCantConnect("Error contacting Univention UDM REST Api ($_path)".($response ? ': '.json_last_error() : ''));
|
||||
throw new UdmCantConnect("Error contacting Univention UDM REST Api ($path)".($response !== false ? ': '.json_last_error() : ''));
|
||||
}
|
||||
curl_close($curl);
|
||||
if (!empty($json['error']))
|
||||
// error in json or non 20x http status
|
||||
if (!empty($json['error']) || !preg_match('|^HTTP/[0-9.]+ 20|', $headers[0]))
|
||||
{
|
||||
error_log(__METHOD__."($path, $_method, ...) returned $response, headers=".json_encode($headers, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
|
||||
error_log(__METHOD__."($path, $_method, ".json_encode($_payload, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE).")");
|
||||
throw new UdmError("UDM REST Api (".urldecode($_path)."): ".(empty($json['error']['message']) ? $response : $json['error']['message']), $json['error']['code']);
|
||||
throw new UdmError("UDM REST Api ($path): ".(empty($json['error']['message']) ? $headers[0] : $json['error']['message']), $json['error']['code']);
|
||||
}
|
||||
if (self::DEBUG)
|
||||
{
|
||||
@ -195,13 +181,54 @@ class Udm
|
||||
$matches = null;
|
||||
if (!isset($headers['location']) || !preg_match('|/([^/]+)$|', $headers['location'], $matches))
|
||||
{
|
||||
throw new UdmMissingLocation("UDM REST Api ($_path) did not return Location header!");
|
||||
throw new UdmMissingLocation("UDM REST Api ($path) did not return Location header!");
|
||||
}
|
||||
return urldecode($matches[1]);
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert header string in array of headers
|
||||
*
|
||||
* A "HTTP/1.1 100 Continue" is NOT returned!
|
||||
*
|
||||
* @param string $head
|
||||
* @return array with name => value pairs, 0: http-status, value can be an array for multiple headers with same name
|
||||
*/
|
||||
protected static function getHeaders($head)
|
||||
{
|
||||
$headers = [];
|
||||
foreach(explode("\r\n", $head) as $header)
|
||||
{
|
||||
if (empty($header)) continue;
|
||||
|
||||
$parts = explode(':', $header, 2);
|
||||
if (count($parts) < 2)
|
||||
{
|
||||
$headers[0] = $header; // http-status
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = strtolower($parts[0]);
|
||||
if (!isset($headers[$name]))
|
||||
{
|
||||
$headers[$name] = trim($parts[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!is_array($headers[$name]))
|
||||
{
|
||||
$headers[$name] = [$headers[$name]];
|
||||
}
|
||||
$headers[$name][] = trim($parts[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self::DEBUG) error_log(__METHOD__."(\$head) returning ".json_encode($headers));
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user
|
||||
*
|
||||
@ -212,7 +239,7 @@ class Udm
|
||||
public function createUser(array $data)
|
||||
{
|
||||
// set default values
|
||||
$payload = $this->user2udm($data, $this->call('users/user/add')['entry']);
|
||||
$payload = $this->user2udm($data, $this->call('users/user/add'));
|
||||
|
||||
$payload['superordinate'] = null;
|
||||
$payload['position'] = $this->config['ldap_context'];
|
||||
@ -236,16 +263,7 @@ class Udm
|
||||
$payload = $this->user2udm($data, $this->call('users/user/'.urlencode($dn), 'GET', [], $get_headers));
|
||||
|
||||
$headers = [];
|
||||
$ret = $this->call('users/user/'.urlencode($dn), 'PUT', $payload, $headers, $get_headers['etag'], true);
|
||||
|
||||
// you can not set the password and force a password change for next login in the same call
|
||||
// the forced password change will be lost --> call again without password to force the change on next login
|
||||
if (!empty($data['account_passwd']) && !empty($data['mustchangepassword']))
|
||||
{
|
||||
unset($data['account_passwd']);
|
||||
$ret = $this->updateUser($ret, $data);
|
||||
}
|
||||
return $ret;
|
||||
return $this->call('users/user/'.urlencode($dn), 'PUT', $payload, $headers, $get_headers['etag'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -315,7 +333,7 @@ class Udm
|
||||
public function createGroup(array $data)
|
||||
{
|
||||
// set default values
|
||||
$payload = $this->group2udm($data, $this->call('groups/group/add')['entry']);
|
||||
$payload = $this->group2udm($data, $this->call('groups/group/add'));
|
||||
|
||||
$payload['superordinate'] = null;
|
||||
$payload['position'] = empty($this->config['ldap_group_context']) ? $this->config['ldap_context'] : $this->config['ldap_group_context'];
|
||||
@ -336,7 +354,7 @@ class Udm
|
||||
{
|
||||
// set existing values
|
||||
$get_headers = [];
|
||||
$payload = $this->user2udm($data, $this->call('groups/group/'.urlencode($dn), 'GET', [], $get_headers));
|
||||
$payload = $this->group2udm($data, $this->call('groups/group/'.urlencode($dn), 'GET', [], $get_headers));
|
||||
|
||||
$headers = [];
|
||||
return $this->call('groups/group/'.urlencode($dn), 'PUT', $payload, $headers, $get_headers['etag'], true);
|
||||
@ -353,7 +371,7 @@ class Udm
|
||||
{
|
||||
foreach([
|
||||
'account_lid' => 'name',
|
||||
'account_id' => ['gidNumber', 'sambaRID'],
|
||||
'account_id' => 'gidNumber',
|
||||
] as $egw => $names)
|
||||
{
|
||||
if (!empty($data[$egw]))
|
||||
@ -364,7 +382,8 @@ class Udm
|
||||
{
|
||||
throw new \Exception ("No '$name' in properties: ".json_encode($payload['properties']));
|
||||
}
|
||||
$payload['properties'][$name] = $data[$egw];
|
||||
// our account_id is negative for groups!
|
||||
$payload['properties'][$name] = $egw === 'account_id' ? abs($data[$egw]) : $data[$egw];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ class Ldap implements Backend
|
||||
isset($allValues[0]['sambapwdlastset']) && (string)$allValues[0]['sambapwdlastset'][0] === '0' ||
|
||||
isset($allValues[0]['krb5passwordend']) && Api\DateTime::user2server($allValues[0]['krb5passwordend'][0]) < time())
|
||||
{
|
||||
error_log(__METHOD__."('$_username') shadowlastchange={$allValues[0]['shadowlastchange']}, sambapwdlastset={$allValues[0]['sambapwdlastset'][0]}, krb5passwordend={$allValues[0]['krb5passwordend'][0]} --> return 0");
|
||||
if ($this->debug) error_log(__METHOD__."('$_username') shadowlastchange={$allValues[0]['shadowlastchange'][0]}, sambapwdlastset={$allValues[0]['sambapwdlastset'][0]}, krb5passwordend={$allValues[0]['krb5passwordend'][0]} --> return 0");
|
||||
return 0;
|
||||
}
|
||||
if (!isset($allValues[0]['shadowlastchange']))
|
||||
|
Loading…
Reference in New Issue
Block a user