Check memberships of newly added entries.
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
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
#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;
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 {
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 )
{
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;
#endif
MO_DANGLING_ERROR,
+ MO_ADDCHECK,
MO_LAST
};
"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 }
};
#if 0
"$ olcMemberOfReverse "
#endif
+ "$ olcMemberOfAddCheck "
") "
")",
Cft_Overlay, mo_cfg, NULL, NULL },
c->value_ad = mo->mo_ad_memberof;
break;
+ case MO_ADDCHECK:
+ c->value_int = MEMBEROF_ADDCHECK( mo );
+ break;
+
default:
assert( 0 );
return 1;
memberof_make_member_filter( mo );
break;
+ case MO_ADDCHECK:
+ mo->mo_flags &= ~MEMBEROF_FADDCHECK;
+ break;
+
default:
assert( 0 );
return 1;
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;
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
+
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