ITS#10167 slapo-memberof: add addcheck option

Check memberships of newly added entries.
This commit is contained in:
Howard Chu 2024-02-01 18:58:50 +00:00 committed by Quanah Gibson-Mount
parent fe7ee15016
commit e992b8972d
4 changed files with 252 additions and 4 deletions

View File

@ -107,6 +107,23 @@ If set to
when an entry containing values of the "is member of" attribute is modified,
the corresponding groups are modified as well.
.TP
.BI "memberof\-addcheck {" true "|" FALSE "}"
This option determines whether the overlay will check newly added
entries for membership in any existing groups. This check is useful
if populated groups are created in the directory before the entries
they reference. The situation often occurs during replication, which
may replicate entries in random order.
If set to
.IR TRUE ,
every Add operation will search for groups referencing the added
entry and populate its memberof attribute with the group DNs. Note
that
.BR memberof\-dangling
must be left on its default setting of
.I ignore
for this option to work.
.LP
The memberof overlay may be used with any backend that provides full
read-write functionality, but it is mainly intended for use
@ -114,10 +131,7 @@ with local storage backends. The maintenance operations it performs
are internal to the server on which the overlay is configured and
are never replicated. Consumer servers should be configured with their
own instances of the memberOf overlay if it is desired to maintain
these memberOf attributes on the consumers. Note that slapo-memberOf
is not compatible with syncrepl based replication, and should not be
used in a replicated environment. An alternative is to use slapo-dynlist
to emulate slapo-memberOf behavior.
these memberOf attributes on the consumers.
.SH FILES
.TP

View File

@ -159,6 +159,7 @@ typedef struct memberof_t {
#define MEMBEROF_FDANGLING_MASK (MEMBEROF_FDANGLING_DROP|MEMBEROF_FDANGLING_ERROR)
#define MEMBEROF_FREFINT 0x04U
#define MEMBEROF_FREVERSE 0x08U
#define MEMBEROF_FADDCHECK 0x10U
ber_int_t mo_dangling_err;
@ -174,6 +175,8 @@ typedef struct memberof_t {
MEMBEROF_CHK((mo),MEMBEROF_FREFINT)
#define MEMBEROF_REVERSE(mo) \
MEMBEROF_CHK((mo),MEMBEROF_FREVERSE)
#define MEMBEROF_ADDCHECK(mo) \
MEMBEROF_CHK((mo),MEMBEROF_FADDCHECK)
} memberof_t;
typedef enum memberof_is_t {
@ -521,6 +524,87 @@ static int memberof_res_delete( Operation *op, SlapReply *rs );
static int memberof_res_modify( Operation *op, SlapReply *rs );
static int memberof_res_modrdn( Operation *op, SlapReply *rs );
typedef struct mo_addcheck_t {
memberof_t *ma_mo;
Entry *ma_e;
Attribute *ma_a;
} mo_addcheck_t;
static int memberof_res_addcheck( Operation *op, SlapReply *rs )
{
mo_addcheck_t *ma = op->o_callback->sc_private;
if ( rs->sr_type == REP_SEARCH ) {
if ( !ma->ma_a ) {
attr_merge_one( ma->ma_e, ma->ma_mo->mo_ad_memberof,
&rs->sr_entry->e_name, &rs->sr_entry->e_nname );
ma->ma_a = attr_find( ma->ma_e->e_attrs, ma->ma_mo->mo_ad_memberof );
} else {
if ( attr_valfind( ma->ma_a, SLAP_MR_EQUALITY | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH |
SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH, &rs->sr_entry->e_nname, NULL, NULL )) {
attr_valadd( ma->ma_a, &rs->sr_entry->e_name, &rs->sr_entry->e_nname, 1 );
}
}
}
return 0;
}
/* Check if an entry being added is already a member of existing groups;
* add those groups to the entry's memberof if any.
*/
static void
memberof_addcheck( Operation *op )
{
slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
memberof_t *mo = (memberof_t *)on->on_bi.bi_private;
Operation o = *op;
Filter mf;
AttributeAssertion mava;
slap_callback sc = {0};
mo_addcheck_t ma;
SlapReply rs = {REP_SEARCH};
o.o_dn = op->o_bd->be_rootdn;
o.o_ndn = op->o_bd->be_rootndn;
o.o_bd->bd_info = (BackendInfo *)on->on_info;
o.o_tag = LDAP_REQ_SEARCH;
o.o_req_dn = op->o_bd->be_suffix[0];
o.o_req_ndn = op->o_bd->be_nsuffix[0];
o.o_do_not_cache = 1;
o.ors_scope = LDAP_SCOPE_SUBTREE;
o.ors_slimit = SLAP_NO_LIMIT;
o.ors_tlimit = SLAP_NO_LIMIT;
o.ors_limit = NULL;
o.ors_attrsonly = 1;
o.ors_attrs = slap_anlist_no_attrs;
mf.f_choice = LDAP_FILTER_EQUALITY;
mf.f_ava = &mava;
mf.f_next = NULL;
mf.f_av_desc = mo->mo_ad_member;
mf.f_av_value = op->o_req_ndn;
o.ors_filter = &mf;
o.ors_filterstr.bv_val = op->o_tmpalloc( mo->mo_ad_member->ad_cname.bv_len + 2
+ op->o_req_ndn.bv_len + 2, op->o_tmpmemctx );
{
char *ptr = o.ors_filterstr.bv_val;
*ptr++ = '(';
ptr = lutil_strcopy( ptr, mo->mo_ad_member->ad_cname.bv_val );
*ptr++ = '=';
ptr = lutil_strcopy( ptr, op->o_req_ndn.bv_val );
*ptr++ = ')';
*ptr = '\0';
}
sc.sc_private = &ma;
sc.sc_response = memberof_res_addcheck;
ma.ma_mo = mo;
ma.ma_e = op->ora_e;
ma.ma_a = attr_find( op->ora_e->e_attrs, mo->mo_ad_memberof );
o.o_callback = ≻
o.o_bd->be_search( &o, &rs );
o.o_bd->bd_info = (BackendInfo *)on;
op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
}
static int
memberof_op_add( Operation *op, SlapReply *rs )
{
@ -549,6 +633,10 @@ memberof_op_add( Operation *op, SlapReply *rs )
return SLAP_CB_CONTINUE;
}
if ( MEMBEROF_ADDCHECK( mo )) {
memberof_addcheck( op );
}
if ( MEMBEROF_REVERSE( mo ) ) {
for ( ap = &op->ora_e->e_attrs; *ap; ap = &(*ap)->a_next ) {
Attribute *a = *ap;
@ -1649,6 +1737,7 @@ enum {
#endif
MO_DANGLING_ERROR,
MO_ADDCHECK,
MO_LAST
};
@ -1730,6 +1819,14 @@ static ConfigTable mo_cfg[] = {
"SYNTAX OMsDirectoryString SINGLE-VALUE )",
NULL, NULL },
{ "memberof-addcheck", "true|FALSE",
2, 2, 0, ARG_MAGIC|ARG_ON_OFF|MO_ADDCHECK, mo_cf_gen,
"( OLcfgOvAt:18.8 NAME 'olcMemberOfAddCheck' "
"DESC 'Check for memberships on added entries' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean SINGLE-VALUE )",
NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};
@ -1749,6 +1846,7 @@ static ConfigOCs mo_ocs[] = {
#if 0
"$ olcMemberOfReverse "
#endif
"$ olcMemberOfAddCheck "
") "
")",
Cft_Overlay, mo_cfg, NULL, NULL },
@ -1887,6 +1985,10 @@ mo_cf_gen( ConfigArgs *c )
c->value_ad = mo->mo_ad_memberof;
break;
case MO_ADDCHECK:
c->value_int = MEMBEROF_ADDCHECK( mo );
break;
default:
assert( 0 );
return 1;
@ -1937,6 +2039,10 @@ mo_cf_gen( ConfigArgs *c )
memberof_make_member_filter( mo );
break;
case MO_ADDCHECK:
mo->mo_flags &= ~MEMBEROF_FADDCHECK;
break;
default:
assert( 0 );
return 1;
@ -2046,6 +2152,15 @@ mo_cf_gen( ConfigArgs *c )
memberof_make_member_filter( mo );
} break;
case MO_ADDCHECK:
if ( c->value_int ) {
mo->mo_flags |= MEMBEROF_FADDCHECK;
} else {
mo->mo_flags &= ~MEMBEROF_FADDCHECK;
}
break;
default:
assert( 0 );
return 1;

View File

@ -339,3 +339,67 @@ sn: person2
memberOfB: cn=group2,ou=Groups,dc=example,dc=com
memberOfC: cn=group1,ou=Groups,dc=example,dc=com
# Re-search the entire database after adding out-of-order groups/users...
dn: dc=example,dc=com
objectClass: organization
objectClass: dcObject
o: Example, Inc.
dc: example
dn: cn=group1,ou=Groups,dc=example,dc=com
objectClass: groupA
cn: group1
memberA: cn=person1,ou=People,dc=example,dc=com
memberA: cn=person2,ou=People,dc=example,dc=com
dn: cn=group2,ou=Groups,dc=example,dc=com
objectClass: groupB
cn: group2
memberB: cn=person1,ou=People,dc=example,dc=com
memberB: cn=person2,ou=People,dc=example,dc=com
dn: cn=group3,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: group3
member: cn=New Person,ou=People,dc=example,dc=com
member: cn=New Group,ou=Groups,dc=example,dc=com
dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups
dn: cn=New Group,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
cn: New Group
member: cn=New Person,ou=People,dc=example,dc=com
memberOf: cn=group3,ou=Groups,dc=example,dc=com
dn: cn=New Person,ou=People,dc=example,dc=com
objectClass: person
cn: New Person
sn: Person
memberOf: cn=group3,ou=Groups,dc=example,dc=com
memberOf: cn=New Group,ou=Groups,dc=example,dc=com
dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People
dn: cn=person1,ou=People,dc=example,dc=com
objectClass: person
objectClass: groupMemberA
objectClass: groupMemberB
cn: person1
sn: person1
memberOfB: cn=group2,ou=Groups,dc=example,dc=com
memberOfC: cn=group1,ou=Groups,dc=example,dc=com
dn: cn=person2,ou=People,dc=example,dc=com
objectClass: person
objectClass: groupMemberA
objectClass: groupMemberB
cn: person2
sn: person2
memberOfB: cn=group2,ou=Groups,dc=example,dc=com
memberOfC: cn=group1,ou=Groups,dc=example,dc=com

View File

@ -441,6 +441,61 @@ if test $RC != 0 ; then
exit $RC
fi
echo "Running ldapmodify to enable add checking..."
$LDAPMODIFY -H $URI1 -D 'cn=config' -w `cat $CONFIGPWF` \
>> $TESTOUT 2>&1 <<EOF
dn: olcOverlay={0}memberof,olcDatabase={1}$BACKEND,cn=config
changetype: modify
replace: olcMemberOfAddCheck
olcMemberOfAddCheck: TRUE
EOF
RC=$?
if test $RC != 0 ; then
echo "ldapmodify failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
fi
echo "Adding group and users out of order..."
$LDAPADD -H $URI1 \
-D "cn=Manager,$BASEDN" -w secret \
>> $TESTOUT 2>&1 <<EOF
dn: cn=group3,ou=Groups,$BASEDN
objectclass: groupOfNames
cn: group3
member: cn=New Person,ou=People,$BASEDN
member: cn=New Group,ou=Groups,$BASEDN
dn: cn=New Group,ou=Groups,$BASEDN
objectclass: groupOfNames
cn: New Group
member: cn=New Person,ou=People,$BASEDN
dn: cn=New Person,ou=People,$BASEDN
objectclass: person
cn: New Person
sn: Person
EOF
RC=$?
if test $RC != 0 ; then
echo "ldapadd failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
fi
echo "Re-search the entire database..."
echo "# Re-search the entire database after adding out-of-order groups/users..." >> $SEARCHOUT
$LDAPSEARCH -S "" -b "$BASEDN" -H $URI1 \
'(objectClass=*)' '*' memberOf >> $SEARCHOUT 2>&1
RC=$?
if test $RC != 0 ; then
echo "ldapsearch failed ($RC)!"
test $KILLSERVERS != no && kill -HUP $KILLPIDS
exit $RC
fi
test $KILLSERVERS != no && kill -HUP $KILLPIDS
LDIF=$MEMBEROFOUT