]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10167 slapo-memberof: add addcheck option
authorHoward Chu <hyc@openldap.org>
Thu, 1 Feb 2024 18:58:50 +0000 (18:58 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 15 Feb 2024 17:56:16 +0000 (17:56 +0000)
Check memberships of newly added entries.

doc/man/man5/slapo-memberof.5
servers/slapd/overlays/memberof.c
tests/data/memberof.out
tests/scripts/test052-memberof

index 45bf1b1c3c43915ad9b207c721a6e8f8e0d469f1..8392f0dad21ed42fe70082b773191f3e28785117 100644 (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
index 5affbbf3f5155094c1e72d4f6f9fafdb27c55045..aed6629ba5154634f619d27e8f7d1840b6578fba 100644 (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 = &sc;
+
+       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;
index 82fb924f8dd4620bdd1166a5cf8d44286f0f65f4..987fd23f01584016575f0af529b4ddaf9525d132 100644 (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
+
index afa5eb96ed6775723c0e21f993e814a3635a8816..4874f87cdc478f257b907ac37605979098e85678 100755 (executable)
@@ -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