From: Alexandre Jousset Date: Wed, 12 Feb 2025 17:46:55 +0000 (+0000) Subject: ITS#10160 - Add "neguri" and "negset" constraint types to slapo-constraint X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=068881efb4de9c4ee227904f4b0b64b21f07214d;p=thirdparty%2Fopenldap.git ITS#10160 - Add "neguri" and "negset" constraint types to slapo-constraint --- diff --git a/doc/man/man5/slapo-constraint.5 b/doc/man/man5/slapo-constraint.5 index 051cf79c48..da1a960c42 100644 --- a/doc/man/man5/slapo-constraint.5 +++ b/doc/man/man5/slapo-constraint.5 @@ -35,14 +35,16 @@ directive. .B constraint_attribute [,...] [ [...]] Specifies the constraint which should apply to the comma-separated attribute list named as the first parameter. -Six types of constraint are currently supported - +Eight types of constraint are currently supported - .BR regex , .BR negregex , .BR size , .BR count , .BR uri , +.BR neguri , +.BR set , and -.BR set . +.BR negset . The parameter following the .B regex @@ -52,12 +54,16 @@ type is a Unix style regular expression (See .BR regex (7) ). The parameter following the .B uri +or +.B neguri type is an LDAP URI. The URI will be evaluated using an internal search. It must not include a hostname, and it must include a list of attributes to evaluate. The parameter following the .B set +or +.B negset type is a string that is interpreted according to the syntax in use for ACL sets. This allows one to construct constraints based on the contents of the entry. @@ -110,6 +116,8 @@ constraint_attribute mail regex ^[[:alnum:]]+@mydomain.com$ constraint_attribute mail negregex ^[[:alnum:]]+@notallowed.com$ constraint_attribute title uri ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog) +constraint_attribute cn neguri + ldap:///ou=People,dc=example,dc=com?cn,sn?sub?(objectClass=inetOrgPerson) constraint_attribute cn,sn,givenName set "(this/givenName + [ ] + this/sn) & this/cn" restrict="ldap:///ou=People,dc=example,dc=com??sub?(objectClass=inetOrgPerson)" @@ -133,6 +141,22 @@ entries in the given scope. (Note that the in a separate database, otherwise the initial set of titleCatalog entries could not be populated while the constraint is in effect.) +With the type "neguri" (negated "uri"), it would +ensure uniqueness of one (or more) attributes against one (or +more) other attributes. A specification like the above +would reject any +.B cn +attributes whose +.B any +values +.B was already listed +in another +.B cn +or +.B sn +attributes of any +.B inetOrgPerson +entries in the given scope. Finally, it requires the values of the attribute .B cn to be constructed by pairing values of the attributes @@ -141,6 +165,8 @@ and .BR givenName , separated by a space, but only for entries derived from the objectClass .BR inetOrgPerson . +Should the type "negset" have been used instead of "set", the +condition would have been reversed. .RE .SH FILES .TP diff --git a/servers/slapd/overlays/constraint.c b/servers/slapd/overlays/constraint.c index 9622c29929..8fd98647f9 100644 --- a/servers/slapd/overlays/constraint.c +++ b/servers/slapd/overlays/constraint.c @@ -42,7 +42,9 @@ #define REGEX_STR "regex" #define NEG_REGEX_STR "negregex" #define URI_STR "uri" +#define NEG_URI_STR "neguri" #define SET_STR "set" +#define NEG_SET_STR "negset" #define SIZE_STR "size" #define COUNT_STR "count" @@ -82,13 +84,15 @@ enum { CONSTRAINT_REGEX, CONSTRAINT_NEG_REGEX, CONSTRAINT_SET, + CONSTRAINT_NEG_SET, CONSTRAINT_URI, + CONSTRAINT_NEG_URI, }; static ConfigDriver constraint_cf_gen; static ConfigTable constraintcfg[] = { - { "constraint_attribute", "attribute[list]> (regex|negregex|uri|set|size|count) []", + { "constraint_attribute", "attribute[list]> (regex|negregex|uri|neguri|set|negset|size|count) []", 4, 0, 0, ARG_MAGIC | CONSTRAINT_ATTRIBUTE, constraint_cf_gen, "( OLcfgOvAt:13.1 NAME 'olcConstraintAttribute' " "DESC 'constraint for list of attributes' " @@ -187,10 +191,18 @@ constraint_cf_gen( ConfigArgs *c ) tstr = SET_STR; quotes = 1; break; + case CONSTRAINT_NEG_SET: + tstr = NEG_SET_STR; + quotes = 1; + break; case CONSTRAINT_URI: tstr = URI_STR; quotes = 1; break; + case CONSTRAINT_NEG_URI: + tstr = NEG_URI_STR; + quotes = 1; + break; default: abort(); } @@ -339,10 +351,10 @@ constraint_cf_gen( ConfigArgs *c ) ap.count = strtoull(c->argv[3], &endptr, 10); if ( *endptr ) rc = ARG_BAD_CONF; - } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 ) { + } else if ( strcasecmp( c->argv[2], URI_STR ) == 0 || strcasecmp( c->argv[2], NEG_URI_STR ) == 0 ) { int err; - ap.type = CONSTRAINT_URI; + ap.type = strcasecmp( c->argv[2], URI_STR ) == 0 ? CONSTRAINT_URI : CONSTRAINT_NEG_URI; err = ldap_url_parse(c->argv[3], &ap.lud); if ( err != LDAP_URL_SUCCESS ) { snprintf( c->cr_msg, sizeof( c->cr_msg ), @@ -421,6 +433,11 @@ constraint_cf_gen( ConfigArgs *c ) ber_str2bv( c->argv[3], 0, 1, &ap.val ); ap.type = CONSTRAINT_SET; + } else if ( strcasecmp( c->argv[2], NEG_SET_STR ) == 0 ) { + ap.set = 1; + ber_str2bv( c->argv[3], 0, 1, &ap.val ); + ap.type = CONSTRAINT_NEG_SET; + } else { snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s %s: Unknown constraint type: %s", @@ -616,7 +633,9 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op ) if (regexec(c->re, bv->bv_val, 0, NULL, 0) != REG_NOMATCH) return LDAP_CONSTRAINT_VIOLATION; /* regular expression violation */ break; - case CONSTRAINT_URI: { + case CONSTRAINT_URI: /* fallthrough */ + case CONSTRAINT_NEG_URI: + { Operation nop = *op; slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; slap_callback cb = { 0 }; @@ -719,7 +738,7 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op ) return rc; /* unexpected error */ } - if (!found) + if (found ^ c->type == CONSTRAINT_URI) return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */ break; } @@ -858,6 +877,10 @@ constraint_add( Operation *op, SlapReply *rs ) if (acl_match_set(&cp->val, op, op->ora_e, NULL) == 0) rc = LDAP_CONSTRAINT_VIOLATION; break; + case CONSTRAINT_NEG_SET: + if (acl_match_set(&cp->val, op, op->ora_e, NULL) != 0) + rc = LDAP_CONSTRAINT_VIOLATION; + break; default: for ( i = 0; b[i].bv_val; i++ ) { rc = constraint_violation( cp, &b[i], op ); @@ -1051,7 +1074,7 @@ constraint_update( Operation *op, SlapReply *rs ) } } - if (cp->type == CONSTRAINT_SET && target_entry) { + if ((cp->type == CONSTRAINT_SET || cp->type == CONSTRAINT_NEG_SET) && target_entry) { if (target_entry_copy == NULL) { Modifications *ml; @@ -1145,7 +1168,7 @@ constraint_update( Operation *op, SlapReply *rs ) } } - if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) { + if ((acl_match_set(&cp->val, op, target_entry_copy, NULL) == 1) ^ (cp->type == CONSTRAINT_SET)) { rc = LDAP_CONSTRAINT_VIOLATION; goto mod_violation; } diff --git a/tests/data/constraint/constraint.out b/tests/data/constraint/constraint.out index d7bcca4bfd..a23cb308a2 100644 --- a/tests/data/constraint/constraint.out +++ b/tests/data/constraint/constraint.out @@ -29,3 +29,7 @@ FAIL FAIL FAIL FAIL +OK +FAIL +FAIL +FAIL diff --git a/tests/data/constraint/root.ldif b/tests/data/constraint/root.ldif index 6c2969060d..55b4bd708d 100644 --- a/tests/data/constraint/root.ldif +++ b/tests/data/constraint/root.ldif @@ -8,6 +8,10 @@ dn: ou=users,dc=example,dc=com ou: users objectclass: organizationalUnit +dn: ou=users2,dc=example,dc=com +ou: users2 +objectclass: organizationalUnit + dn: ou=groups,dc=example,dc=com ou: groups objectclass: organizationalUnit diff --git a/tests/data/constraint/tn_fail_01.ldif b/tests/data/constraint/tn_fail_01.ldif new file mode 100644 index 0000000000..86b9fd7977 --- /dev/null +++ b/tests/data/constraint/tn_fail_01.ldif @@ -0,0 +1,4 @@ +dn: givenName=John,ou=users2,dc=example,dc=com +changetype: modify +replace: cn +cn: John Rouge diff --git a/tests/data/constraint/tn_fail_02.ldif b/tests/data/constraint/tn_fail_02.ldif new file mode 100644 index 0000000000..322e1261b1 --- /dev/null +++ b/tests/data/constraint/tn_fail_02.ldif @@ -0,0 +1,4 @@ +dn: givenName=John,ou=users2,dc=example,dc=com +changetype: modify +replace: sn +sn: le Rouge diff --git a/tests/data/constraint/tn_fail_03.ldif b/tests/data/constraint/tn_fail_03.ldif new file mode 100644 index 0000000000..d6d43ab479 --- /dev/null +++ b/tests/data/constraint/tn_fail_03.ldif @@ -0,0 +1,4 @@ +dn: givenName=John,ou=users2,dc=example,dc=com +changetype: modify +replace: uid +uid: 1 diff --git a/tests/data/constraint/tn_ok_01.ldif b/tests/data/constraint/tn_ok_01.ldif new file mode 100644 index 0000000000..4a53a7ba65 --- /dev/null +++ b/tests/data/constraint/tn_ok_01.ldif @@ -0,0 +1,4 @@ +dn: givenName=John,ou=users2,dc=example,dc=com +changetype: modify +replace: uid +uid: 4 diff --git a/tests/data/constraint/user2.ldif b/tests/data/constraint/user2.ldif new file mode 100644 index 0000000000..a7b9facb4d --- /dev/null +++ b/tests/data/constraint/user2.ldif @@ -0,0 +1,10 @@ +dn: givenName=John,ou=users2,dc=example,dc=com +objectclass: inetOrgPerson +objectclass: organizationalPerson +cn: John le Rouge +givenname: John +sn: Rouge +mail: original@example.com +description: desc1 +description: desc2 +uid: 3 diff --git a/tests/scripts/test064-constraint b/tests/scripts/test064-constraint index 952370b947..20a8744d7f 100755 --- a/tests/scripts/test064-constraint +++ b/tests/scripts/test064-constraint @@ -24,9 +24,11 @@ fi CONSTRAINTDIR="$DATADIR/constraint" ROOTLDIF="$CONSTRAINTDIR/root.ldif" USERLDIF="$CONSTRAINTDIR/user.ldif" +USER2LDIF="$CONSTRAINTDIR/user2.ldif" RESULTOUT="$CONSTRAINTDIR/constraint.out" SCRIPTOUT="$TESTDIR/constraint.out" USERDN="cn=John Doe,ou=users,$BASEDN" +USER2DN="givenName=John,ou=users2,$BASEDN" CONFDIR=$TESTDIR/slapd.d mkdir -p $TESTDIR $CONFDIR $DBDIR1 @@ -117,6 +119,12 @@ olcConstraintAttribute: cn,sn,givenName olcConstraintAttribute: uid uri "ldap:///ou=groups,$BASEDN?uid?one?(objectClass=inetOrgPerson)" restrict="ldap:///ou=users,$BASEDN??one" +olcConstraintAttribute: cn,sn,givenName + negset "(this/givenName + [ ] + this/sn) & this/cn" + restrict="ldap:///$USER2DN??sub?(objectClass=inetOrgPerson)" +olcConstraintAttribute: uid + neguri "ldap:///ou=groups,$BASEDN?uid?one?(objectClass=inetOrgPerson)" + restrict="ldap:///ou=users2,$BASEDN??one" EOF $SLAPADD -F $CONFDIR -n 0 -l $TESTDIR/config.ldif @@ -168,9 +176,16 @@ if test $RC != 0 ; then test $KILLSERVERS != no && kill -HUP $PID exit $RC fi +$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $USER2LDIF >/dev/null 2>&1 +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $PID + exit $RC +fi echo "Running constraint tests..." -for ldif in $CONSTRAINTDIR/*ok*.ldif $CONSTRAINTDIR/*fail*.ldif; do +for ldif in $CONSTRAINTDIR/t_ok*.ldif $CONSTRAINTDIR/t_fail*.ldif; do ### reload $LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD "$USERDN" >/dev/null 2>&1 RC=$? @@ -202,6 +217,39 @@ for ldif in $CONSTRAINTDIR/*ok*.ldif $CONSTRAINTDIR/*fail*.ldif; do fi done +echo "Running *neg* constraint tests..." +for ldif in $CONSTRAINTDIR/tn_ok*.ldif $CONSTRAINTDIR/tn_fail*.ldif; do + ### reload + $LDAPDELETE -D "$MANAGERDN" -H $URI1 -w $PASSWD "$USER2DN" >/dev/null 2>&1 + RC=$? + if test $RC != 0 ; then + echo "ldapdelete failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $PID + exit $RC + fi + $LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD -f $USER2LDIF >/dev/null 2>&1 + RC=$? + if test $RC != 0 ; then + echo "ldapadd failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $PID + exit $RC + fi + + ### info + echo -n " [$ldif]: " + + ### modify + $LDAPMODIFY -H $URI1 -x -D "$MANAGERDN" -f $ldif -w $PASSWD >/dev/null 2>&1 + RC=$? + if test $RC = 0 ; then + echo "OK" | tee -a $SCRIPTOUT + elif test $RC = 19 ; then + echo "FAIL" | tee -a $SCRIPTOUT + else + echo "UNEXPECTED ($RC)" + fi +done + echo "Comparing output..." $DIFF $SCRIPTOUT $RESULTOUT > $CMPOUT RC=$?