]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#10160 - Add "neguri" and "negset" constraint types to slapo-constraint
authorAlexandre Jousset <mid@gtmp.org>
Wed, 12 Feb 2025 17:46:55 +0000 (17:46 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Wed, 12 Feb 2025 17:46:55 +0000 (17:46 +0000)
doc/man/man5/slapo-constraint.5
servers/slapd/overlays/constraint.c
tests/data/constraint/constraint.out
tests/data/constraint/root.ldif
tests/data/constraint/tn_fail_01.ldif [new file with mode: 0644]
tests/data/constraint/tn_fail_02.ldif [new file with mode: 0644]
tests/data/constraint/tn_fail_03.ldif [new file with mode: 0644]
tests/data/constraint/tn_ok_01.ldif [new file with mode: 0644]
tests/data/constraint/user2.ldif [new file with mode: 0644]
tests/scripts/test064-constraint

index 051cf79c48d4fc436e525e343a1438a28973317b..da1a960c4221b6a31f156cf5679f2cd5542855aa 100644 (file)
@@ -35,14 +35,16 @@ directive.
 .B constraint_attribute <attribute_name>[,...] <type> <value> [<extra> [...]]
 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
index 9622c29929ad2f6bd5c509a9a74b99c05e3eb259..8fd98647f94f1bd537667b4b42e05152f4251856 100644 (file)
@@ -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) <value> [<restrict URI>]",
+       { "constraint_attribute", "attribute[list]> (regex|negregex|uri|neguri|set|negset|size|count) <value> [<restrict URI>]",
          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;
                                }
index d7bcca4bfd3605c9c1f6a3f1025577c98c194bad..a23cb308a2d10b934b14471a669e21ddd2a2975d 100644 (file)
@@ -29,3 +29,7 @@ FAIL
 FAIL
 FAIL
 FAIL
+OK
+FAIL
+FAIL
+FAIL
index 6c2969060def0518ec74290605610b1a20933c1e..55b4bd708d31b3f14f45c2110f5e06b11acf9ea5 100644 (file)
@@ -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 (file)
index 0000000..86b9fd7
--- /dev/null
@@ -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 (file)
index 0000000..322e126
--- /dev/null
@@ -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 (file)
index 0000000..d6d43ab
--- /dev/null
@@ -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 (file)
index 0000000..4a53a7b
--- /dev/null
@@ -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 (file)
index 0000000..a7b9fac
--- /dev/null
@@ -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
index 952370b947bee13d5a77ec460692493fb6129d7d..20a8744d7fe1df7ca8171d0a6006f8916ed6949c 100755 (executable)
@@ -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=$?