Commit 4067bf36 authored by Côme Chilliet's avatar Côme Chilliet

feat(acl) Add target ACL filter and fix user ACL filter

issue #5531
parent 2291c070
......@@ -74,20 +74,29 @@ class acl
static function explodeACL ($acl)
{
$list = explode(':', $acl);
if (count($list) == 5) {
list($index, $type, $role, $members, $filter) = $list;
$filter = base64_decode($filter);
if (count($list) == 6) {
list($index, $type, $role, $members, $userfilter, $targetfilter) = $list;
$userfilter = base64_decode($userfilter);
$targetfilter = base64_decode($targetfilter);
} elseif (count($list) == 5) {
list($index, $type, $role, $members, $userfilter) = $list;
$userfilter = base64_decode($userfilter);
$targetfilter = '';
} else {
$filter = "";
list($index, $type, $role, $members) = $list;
$userfilter = '';
$targetfilter = '';
}
$a = [ $index => [
'type' => $type,
'filter' => $filter,
'members' => acl::extractMembers($members),
'acl' => base64_decode($role),
]];
$a = [
$index => [
'type' => $type,
'userfilter' => $userfilter,
'targetfilter' => $targetfilter,
'members' => acl::extractMembers($members),
'acl' => base64_decode($role),
]
];
/* Handle unknown types */
if (!in_array($type, ['subtree', 'base'])) {
......@@ -104,7 +113,7 @@ class acl
*
* \return an array with members
*/
static function extractMembers ($ms)
static function extractMembers (string $ms)
{
global $config;
$a = [];
......@@ -158,7 +167,7 @@ class acl
*
* \param string $acl The acl to be extracted
*/
static function extractACL ($acl)
static function extractACL (string $acl)
{
/* Rip acl off the string, seperate by ',' and place it in an array */
$as = preg_replace('/^[^:]+:[^:]+:[^:]*:([^:]*).*$/', '\1', $acl);
......
......@@ -46,14 +46,16 @@ class userinfo
var $givenName = '';
var $gidNumber = -1;
var $language = "";
var $subtreeACL = [];
var $ACL = [];
var $groups = [];
var $roles = [];
var $result_cache = [];
var $ignoreACL = FALSE;
var $ACLperPath = [];
/*! \brief LDAP attributes of this user at login */
protected $cachedAttrs = [];
protected $result_cache = [];
protected $ignoreACL = FALSE;
protected $ACL = [];
protected $ACLperPath = [];
/*! \brief LDAP size limit handler */
protected $sizeLimitHandler;
......@@ -64,8 +66,6 @@ class userinfo
/*! \brief Password change should be forced */
protected $forcePasswordChange = FALSE;
/* get acl's an put them into the userinfo object
attr subtreeACL (userdn:components, userdn:component1#sub1#sub2,component2,...) */
function __construct ($userdn)
{
global $config;
......@@ -85,7 +85,7 @@ class userinfo
{
global $config;
$ldap = $config->get_ldap_link();
$ldap->cat($this->dn, ['cn', 'sn', 'givenName', 'uid', 'gidNumber', 'preferredLanguage']);
$ldap->cat($this->dn, ['*']);
$attrs = $ldap->fetch();
$this->uid = $attrs['uid'][0];
......@@ -111,6 +111,8 @@ class userinfo
if (isset($attrs['preferredLanguage'][0])) {
$this->language = $attrs['preferredLanguage'][0];
}
$this->cachedAttrs = $attrs;
}
/*!
......@@ -155,88 +157,117 @@ class userinfo
}
/* Crawl through ACLs and move relevant to the tree */
$ldap->search("(objectClass=gosaACL)", ['dn', 'gosaAclEntry']);
$aclp = [];
$aclc = [];
$ldap->search('(objectClass=gosaACL)', ['dn', 'gosaAclEntry']);
$ACLsContent = [];
while ($attrs = $ldap->fetch()) {
/* Insert links in ACL array */
$aclp[$attrs['dn']] = substr_count($attrs['dn'], ',');
$ol = [];
$mergedAcls = [];
for ($i = 0; $i < $attrs['gosaAclEntry']['count']; $i++) {
$ol = array_merge($ol, acl::explodeAcl($attrs['gosaAclEntry'][$i]));
$mergedAcls = array_merge($mergedAcls, acl::explodeACL($attrs['gosaAclEntry'][$i]));
}
$aclc[$attrs['dn']] = $ol;
$ACLsContent[$attrs['dn']] = $mergedAcls;
}
$ACLsContentResolved = [];
/* Resolve roles here */
foreach ($aclc as $dn => $data) {
foreach ($data as $prio => $aclc_value) {
unset($aclc[$dn][$prio]);
$ldap->cat($aclc_value['acl'], ["gosaAclTemplate"]);
foreach ($ACLsContent as $dn => $ACLRules) {
foreach ($ACLRules as $ACLRule) {
$ldap->cat($ACLRule['acl'], ['gosaAclTemplate']);
$attrs = $ldap->fetch();
if (isset($attrs['gosaAclTemplate'])) {
$roleAcls = acl::explodeRole($attrs['gosaAclTemplate']);
foreach ($roleAcls as $roleAcl) {
$aclc[$dn][] = [
'acl' => $roleAcl,
'type' => $aclc_value['type'],
'members' => $aclc_value['members'],
'filter' => $aclc_value['filter']
];
}
if (!isset($attrs['gosaAclTemplate'])) {
continue;
}
}
}
/* ACL's read, sort for tree depth */
asort($aclp);
/* Sort in tree order */
foreach ($aclp as $dn => $acl) {
/* Check if we need to keep this ACL */
foreach ($aclc[$dn] as $idx => $type) {
$interesting = FALSE;
/* No members? This ACL rule is deactivated ... */
if (count($type['members'])) {
/* Inspect members... */
foreach (array_keys($type['members']) as $grp) {
/* Some group inside the members that is relevant for us? */
if (in_array_ics(preg_replace('/^G:/', '', $grp), $this->groups)) {
$interesting = TRUE;
break;
}
/* Inspect members... */
foreach (array_keys($ACLRule['members']) as $member) {
/* Wildcard? */
if ($member === 'G:*') {
$interesting = TRUE;
break;
}
/* Some role inside the members that is relevant for us? */
if (in_array_ics(preg_replace('/^R:/', '', $grp), $this->roles)) {
$interesting = TRUE;
list($memberType, $memberDn) = explode(':', $member, 2);
switch ($memberType) {
case 'G':
if (in_array_ics($memberDn, $this->groups)) {
$interesting = TRUE;
break 2;
}
break;
}
/* User inside the members? */
if (mb_strtoupper(preg_replace('/^U:/', '', $grp)) == mb_strtoupper($this->dn)) {
$interesting = TRUE;
case 'R':
if (in_array_ics($memberDn, $this->roles)) {
$interesting = TRUE;
break 2;
}
break;
}
/* Wildcard? */
if (preg_match('/^G:\*/', $grp)) {
$interesting = TRUE;
case 'U':
if (mb_strtolower($memberDn) === mb_strtolower($this->dn)) {
$interesting = TRUE;
break 2;
}
break;
}
default:
throw FusionDirectoryException('Unknown ACL member type '.$memberType);
}
}
if (!$interesting) {
continue;
}
if (!empty($ACLRule['userfilter'])) {
/* Check if we match the user filter */
if (!$ldap->object_match_filter($this->dn, $ACLRule['userfilter'])) {
  • Merge this if statement with the enclosing one. 📘

Please register or sign in to reply
continue;
}
}
if ($interesting) {
if (!isset($this->ACL[$dn])) {
$this->ACL[$dn] = [];
if (!empty($ACLRule['targetfilter'])) {
$ldap->cd($dn);
$targetFilter = templateHandling::parseString($ACLRule['targetfilter'], $this->cachedAttrs, 'ldap_escape_f');
$ldap->search($targetFilter, ['dn']);
$targetDns = [];
while ($targetAttrs = $ldap->fetch()) {
$targetDns[] = $targetAttrs['dn'];
}
$this->ACL[$dn][$idx] = $type;
} else {
$targetDns = [$dn];
}
$roleAcls = acl::explodeRole($attrs['gosaAclTemplate']);
foreach ($roleAcls as $roleAcl) {
foreach ($targetDns as $targetDn) {
$ACLsContentResolved[$targetDn][] = [
'acl' => $roleAcl,
'type' => $ACLRule['type'],
'members' => $ACLRule['members'],
];
}
}
}
}
/* Sort by tree depth */
uksort(
$ACLsContentResolved,
function ($dn1, $dn2)
{
return substr_count($dn1, ',') <=> substr_count($dn2, ',');
}
);
/* Insert in $this->ACL */
foreach ($ACLsContentResolved as $dn => $ACLRules) {
foreach ($ACLRules as $idx => $ACLRule) {
if (!isset($this->ACL[$dn])) {
$this->ACL[$dn] = [];
}
$this->ACL[$dn][$idx] = $ACLRule;
}
}
......@@ -539,17 +570,6 @@ class userinfo
continue;
}
/* With user filter */
if (!empty($subacl['filter'])) {
$id = $dn."-".$subacl['filter'];
if (!isset($ACL_CACHE['FILTER'][$id])) {
$ACL_CACHE['FILTER'][$id] = $ldap->object_match_filter($dn, $subacl['filter']);
}
if (!$ACL_CACHE['FILTER'][$id]) {
continue;
}
}
/* Self ACLs? */
if (($dn != $this->dn) && isset($subacl['acl'][$object][0]) && (strpos($subacl['acl'][$object][0], "s") !== FALSE)) {
continue;
......@@ -747,7 +767,7 @@ class userinfo
*
* \param $newACL The new ACL
*/
function mergeACL ($acl, $type, $newACL)
function mergeACL (array $acl, $type, $newACL)
{
$at = ["subtree" => "s", "one" => "1"];
......@@ -1240,7 +1260,7 @@ class userinfo
}
}
/* Username is set, load subtreeACL's now */
/* Username is set, load ACLs now */
$ui->loadACL();
return $ui;
......
......@@ -41,10 +41,11 @@ class ACLsAssignmentAttribute extends DialogOrderedArrayAttribute
{
$acl = explode(':', $value);
return [$acl[0], [
'scope' => $acl[1],
'role' => base64_decode($acl[2]),
'members' => array_map('base64_decode', explode(',', $acl[3])),
'userfilter' => (isset($acl[4]) ? base64_decode($acl[4]) : ''),
'scope' => $acl[1],
'role' => base64_decode($acl[2]),
'members' => array_map('base64_decode', explode(',', $acl[3])),
'userfilter' => (isset($acl[4]) ? base64_decode($acl[4]) : ''),
'targetfilter' => (isset($acl[5]) ? base64_decode($acl[5]) : ''),
]];
}
......@@ -54,7 +55,8 @@ class ACLsAssignmentAttribute extends DialogOrderedArrayAttribute
':'.$value['scope'].
':'.base64_encode($value['role']).
':'.join(',', array_map('base64_encode', $value['members'])).
':'.base64_encode($value['userfilter']);
':'.base64_encode($value['userfilter']).
':'.base64_encode($value['targetfilter']);
}
function foreignKeyUpdate ($oldvalue, $newvalue, array $source)
......
......@@ -65,6 +65,10 @@ class aclAssignmentDialogWindow extends simplePlugin
_('Restrict users with filter'), _('LDAP filter which a member must match to actually get the rights'),
'aclUserFilter', FALSE
),
new StringAttribute(
_('Restrict targets with filter'), _('LDAP filter which a dn must match to actually be concerned. May use %dn% mask for user dn. Example: (manager=%dn%).'),
'aclTargetFilter', FALSE
),
]
],
];
......@@ -98,7 +102,8 @@ class aclAssignmentDialogWindow extends simplePlugin
if ($value['members'][0] == '*') {
$this->allUsers = TRUE;
}
$this->aclUserFilter = $value['userfilter'];
$this->aclUserFilter = $value['userfilter'];
$this->aclTargetFilter = $value['targetfilter'];
}
}
......@@ -120,10 +125,11 @@ class aclAssignmentDialogWindow extends simplePlugin
function getAclEntry ()
{
$entry = [
'scope' => $this->aclMode,
'role' => $this->aclRole,
'members' => $this->aclMembers,
'userfilter' => $this->aclUserFilter,
'scope' => $this->aclMode,
'role' => $this->aclRole,
'members' => $this->aclMembers,
'userfilter' => $this->aclUserFilter,
'targetfilter' => $this->aclTargetFilter,
];
if ($this->allUsers) {
$entry['members'] = ['*'];
......
  • SonarQube analysis reported 2 issues

    • 2 major

    Watch the comments in this conversation to review them.

    1 extra issue

    Note: The following issues were found on lines that were not modified in the commit. Because these issues can't be reported as line comments, they are summarized here:

    1. Remove this unused "$ldap" local variable. 📘
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment