* ActiveDirectory: allow to configure an optional group-context

also fixes problems with Univention AD storing stock groups (eg. Domain Users) in a different OU=Groups than users
This commit is contained in:
ralf 2022-06-30 16:44:28 +02:00
parent 76a19e86fb
commit 242e055f53
2 changed files with 43 additions and 16 deletions

View File

@ -253,22 +253,32 @@ class Ads
const DOMAIN_USERS_GROUP = 513; const DOMAIN_USERS_GROUP = 513;
const ADS_CONTEXT = 'ads_context'; const ADS_CONTEXT = 'ads_context';
const ADS_GROUP_CONTEXT = 'ads_group_context';
/** /**
* Get context for user and group objects * Get context for user and group objects
* *
* Can be set via server-config "ads_context", otherwise baseDN is used * Can be set via server-config "ads_context" and "ads_group_context", otherwise baseDN is used
* *
* @param boolean $set_if_empty =false true set from DN of "Domain Users" group # * @param boolean $set_if_empty =false true set from DN of "Domain Users" group #
* @param bool|null $user true: user, false: group, null: both
* @return string * @return string
*/ */
public function ads_context($set_if_empty=false) public function ads_context($set_if_empty=false, bool $user=null)
{ {
if (empty($this->frontend->config[self::ADS_CONTEXT])) if (empty($this->frontend->config[self::ADS_CONTEXT]))
{ {
if ($set_if_empty && ($dn = $this->id2name(-self::DOMAIN_USERS_GROUP, 'account_dn'))) if ($set_if_empty && ($dn = $this->id2name(-self::DOMAIN_USERS_GROUP, 'account_dn')))
{ {
$dn = preg_replace('/^CN=.*?,(CN|OU)=/i', '$1=', $dn); $dn = preg_replace('/^CN=.*?,(CN|OU)=/i', '$1=', $dn);
// Univention AD uses container OU=Groups for the stock groups, not like standard AD using OU=Users for both
// save that as group context and generate OU=Users as user context from it
if (preg_match('/^(CN|OU)=Groups,/i', $dn))
{
Api\Config::save_value(self::ADS_GROUP_CONTEXT, $this->frontend->config[self::ADS_GROUP_CONTEXT]=$dn, 'phpgwapi');
$dn = preg_replace('/^(CN|OU)=(.*?),/i', '$1=Users,', $dn);
}
Api\Config::save_value(self::ADS_CONTEXT, $this->frontend->config[self::ADS_CONTEXT]=$dn, 'phpgwapi'); Api\Config::save_value(self::ADS_CONTEXT, $this->frontend->config[self::ADS_CONTEXT]=$dn, 'phpgwapi');
} }
else else
@ -276,19 +286,31 @@ class Ads
return $this->adldap->getBaseDn(); return $this->adldap->getBaseDn();
} }
} }
return $this->frontend->config[self::ADS_CONTEXT]; // if we want and have a group context, use it
if ($user === false && !empty($this->frontend->config[self::ADS_GROUP_CONTEXT]))
{
return $this->frontend->config[self::ADS_GROUP_CONTEXT];
}
// if we have a user-context and no group-context, use it
if (empty($this->frontend->config[self::ADS_GROUP_CONTEXT]) && !empty($this->frontend->config[self::ADS_CONTEXT]))
{
return $this->frontend->config[self::ADS_CONTEXT];
}
// otherwise use base DN
return $this->adldap->getBaseDn();
} }
/** /**
* Get container for new user and group objects * Get container for new user and group objects
* *
* Can be set via server-config "ads_context", otherwise parent of DN from "Domain Users" is used * Can be set via server-config "ads_context" and "ads_group", otherwise parent of DN from "Domain Users" is used
* *
* @param bool $user true: user, false: group, null: both
* @return string * @return string
*/ */
protected function _get_container() protected function _get_container(bool $user)
{ {
$context = $this->ads_context(true); $context = $this->ads_context(true, $user);
$base = $this->adldap->getBaseDn(); $base = $this->adldap->getBaseDn();
$matches = null; $matches = null;
if (!preg_match('/^(.*),'.preg_quote($base, '/').'$/i', $context, $matches)) if (!preg_match('/^(.*),'.preg_quote($base, '/').'$/i', $context, $matches))
@ -652,7 +674,7 @@ class Ads
{ {
$attributes[$adldap] = (string)$data[$egw]; $attributes[$adldap] = (string)$data[$egw];
} }
$attributes['container'] = $this->_get_container(); $attributes['container'] = $this->_get_container(false);
$ret = $this->adldap->group()->create($attributes); $ret = $this->adldap->group()->create($attributes);
if ($ret !== true) if ($ret !== true)
@ -748,7 +770,7 @@ class Ads
if (isset($data[$egw])) $attributes[$adldap] = $data[$egw]; if (isset($data[$egw])) $attributes[$adldap] = $data[$egw];
} }
$attributes['enabled'] = !isset($data['account_status']) || $data['account_status'] === 'A'; $attributes['enabled'] = !isset($data['account_status']) || $data['account_status'] === 'A';
$attributes['container'] = $this->_get_container(); $attributes['container'] = $this->_get_container(true);
$ret = $this->adldap->user()->create($attributes); $ret = $this->adldap->user()->create($attributes);
if ($ret !== true) if ($ret !== true)

View File

@ -410,37 +410,42 @@
</tr> </tr>
<tr class="row_on"> <tr class="row_on">
<td> <td>
{lang_Context_to_create_users}:<br/> {lang_Context_to_create_users}: ({lang_leave_empty_to_use_default})<br/>
{lang_eg._"CN=Users,DC=domain,DC=com"_for_ADS_domain_"domain.com"}<br/> {lang_eg._"CN=Users,DC=domain,DC=com"_for_ADS_domain_"domain.com"}<br/>
({lang_leave_empty_to_use_default})
</td> </td>
<td><input name="newsettings[ads_context]" value="{value_ads_context}" size="80" /></td> <td><input name="newsettings[ads_context]" value="{value_ads_context}" size="80" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_off">
<td>
{lang_Group_context}: ({lang_leave_empty_to_use_default})
</td>
<td><input name="newsettings[ads_group_context]" value="{value_ads_group_context}" size="80" /></td>
</tr>
<tr class="row_on">
<td>{lang_Additional_user_filter_(optional)}:</td> <td>{lang_Additional_user_filter_(optional)}:</td>
<td><input name="newsettings[ads_user_filter]" value="{value_ads_user_filter}" size="80" /></td> <td><input name="newsettings[ads_user_filter]" value="{value_ads_user_filter}" size="80" /></td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>{lang_Additional_group_filter_(optional)}:</td> <td>{lang_Additional_group_filter_(optional)}:</td>
<td><input name="newsettings[ads_group_filter]" value="{value_ads_group_filter}" size="80" /></td> <td><input name="newsettings[ads_group_filter]" value="{value_ads_group_filter}" size="80" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td><b>{lang_Attributes_for_new_users}</b><br/></td> <td><b>{lang_Attributes_for_new_users}</b><br/></td>
<td>{lang_use_%u_for_username,_leave_empty_to_no_set}</td> <td>{lang_use_%u_for_username,_leave_empty_to_no_set}</td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>profilePath</td> <td>profilePath</td>
<td><input name="newsettings[ads_new_profilePath]" value="{value_ads_new_profilePath}" size="40" /></td> <td><input name="newsettings[ads_new_profilePath]" value="{value_ads_new_profilePath}" size="40" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td>homeDirectory</td> <td>homeDirectory</td>
<td><input name="newsettings[ads_new_homeDirectory]" value="{value_ads_new_homeDirectory}" size="40" /></td> <td><input name="newsettings[ads_new_homeDirectory]" value="{value_ads_new_homeDirectory}" size="40" /></td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>homeDrive</td> <td>homeDrive</td>
<td><input name="newsettings[ads_new_homeDrive]" value="{value_ads_new_homeDrive}" size="40" /></td> <td><input name="newsettings[ads_new_homeDrive]" value="{value_ads_new_homeDrive}" size="40" /></td>
</tr> </tr>
<tr class="row_off"> <tr class="row_on">
<td>scriptPath</td> <td>scriptPath</td>
<td><input name="newsettings[ads_new_scriptPath]" value="{value_ads_new_scriptPath}" size="40" /></td> <td><input name="newsettings[ads_new_scriptPath]" value="{value_ads_new_scriptPath}" size="40" /></td>
</tr> </tr>