ITS#10338 Add olcConstraintAllowEmpty

This commit is contained in:
Ondřej Kuzník 2025-06-11 14:11:20 +01:00 committed by Quanah Gibson-Mount
parent 10b1d6c9b4
commit 87a2d70750
2 changed files with 64 additions and 12 deletions

View file

@ -25,10 +25,9 @@ No constraints are applied for operations performed with the
.I relax .I relax
control set. control set.
.SH CONFIGURATION .SH CONFIGURATION
This These
.B slapd.conf .B slapd.conf
option applies to the constraint overlay. options apply to the constraint overlay. They should appear after the
It should appear after the
.B overlay .B overlay
directive. directive.
.TP .TP
@ -100,6 +99,18 @@ is present; it defaults to
The other parameters of the URI are not allowed. The other parameters of the URI are not allowed.
.RE .RE
.TP
.B constraint_allowempty FALSE | TRUE
Historically,
.B slapo-constraint
rejected Modify/Add requests with an empty sequence of modifications. This was
not intentional and if this option is
.BR TRUE ,
such modifications will be allowed so long as they are otherwise valid.
The default is
.B FALSE
to maintain backwards compatibility.
.LP .LP
Any attempt to add or modify an attribute named as part of the Any attempt to add or modify an attribute named as part of the
constraint overlay specification which does not fit the constraint overlay specification which does not fit the

View file

@ -77,6 +77,11 @@ typedef struct constraint {
struct berval filter; struct berval filter;
} constraint; } constraint;
typedef struct constraint_info {
struct constraint *constraint;
int allow_empty;
} constraint_info;
enum { enum {
CONSTRAINT_ATTRIBUTE = 1, CONSTRAINT_ATTRIBUTE = 1,
CONSTRAINT_COUNT, CONSTRAINT_COUNT,
@ -87,6 +92,7 @@ enum {
CONSTRAINT_NEG_SET, CONSTRAINT_NEG_SET,
CONSTRAINT_URI, CONSTRAINT_URI,
CONSTRAINT_NEG_URI, CONSTRAINT_NEG_URI,
CONSTRAINT_ALLOWEMPTY,
}; };
static ConfigDriver constraint_cf_gen; static ConfigDriver constraint_cf_gen;
@ -98,6 +104,13 @@ static ConfigTable constraintcfg[] = {
"DESC 'constraint for list of attributes' " "DESC 'constraint for list of attributes' "
"EQUALITY caseIgnoreMatch " "EQUALITY caseIgnoreMatch "
"SYNTAX OMsDirectoryString )", NULL, NULL }, "SYNTAX OMsDirectoryString )", NULL, NULL },
{ "constraint_allowempty", "on|off", 1, 2, 0,
ARG_ON_OFF | ARG_OFFSET | CONSTRAINT_ALLOWEMPTY,
(void *)offsetof(constraint_info,allow_empty),
"( OLcfgOvAt:13.2 NAME 'olcConstraintAllowEmpty' "
"DESC 'are empty modify requests allowed?' "
"EQUALITY booleanMatch "
"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
{ NULL, NULL, 0, 0, 0, ARG_IGNORED } { NULL, NULL, 0, 0, 0, ARG_IGNORED }
}; };
@ -106,7 +119,7 @@ static ConfigOCs constraintocs[] = {
"NAME 'olcConstraintConfig' " "NAME 'olcConstraintConfig' "
"DESC 'Constraint overlay configuration' " "DESC 'Constraint overlay configuration' "
"SUP olcOverlayConfig " "SUP olcOverlayConfig "
"MAY ( olcConstraintAttribute ) )", "MAY ( olcConstraintAttribute $ olcConstraintAllowEmpty ) )",
Cft_Overlay, constraintcfg }, Cft_Overlay, constraintcfg },
{ NULL, 0, NULL } { NULL, 0, NULL }
}; };
@ -142,7 +155,8 @@ static int
constraint_cf_gen( ConfigArgs *c ) constraint_cf_gen( ConfigArgs *c )
{ {
slap_overinst *on = (slap_overinst *)(c->bi); slap_overinst *on = (slap_overinst *)(c->bi);
constraint *cn = on->on_bi.bi_private, *cp; constraint_info *ov = on->on_bi.bi_private;
constraint *cn = ov->constraint, *cp;
struct berval bv; struct berval bv;
int i, rc = 0; int i, rc = 0;
constraint ap = { NULL }; constraint ap = { NULL };
@ -272,7 +286,7 @@ constraint_cf_gen( ConfigArgs *c )
cn = cp; cn = cp;
} }
on->on_bi.bi_private = NULL; ov->constraint = NULL;
} else { } else {
constraint **cpp; constraint **cpp;
@ -286,7 +300,7 @@ constraint_cf_gen( ConfigArgs *c )
*cpp = cp->ap_next; *cpp = cp->ap_next;
constraint_free( cp, 1 ); constraint_free( cp, 1 );
} }
on->on_bi.bi_private = cn; ov->constraint = cn;
} }
break; break;
@ -575,7 +589,7 @@ done:;
a2->restrict_filter = ap.restrict_filter; a2->restrict_filter = ap.restrict_filter;
a2->restrict_val = ap.restrict_val; a2->restrict_val = ap.restrict_val;
for ( app = (constraint **)&on->on_bi.bi_private; *app; app = &(*app)->ap_next ) for ( app = &ov->constraint; *app; app = &(*app)->ap_next )
/* Get to the end */ ; /* Get to the end */ ;
a2->ap_next = *app; a2->ap_next = *app;
@ -828,8 +842,9 @@ static int
constraint_add( Operation *op, SlapReply *rs ) constraint_add( Operation *op, SlapReply *rs )
{ {
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
constraint_info *ov = on->on_bi.bi_private;
constraint *c = ov->constraint, *cp;
Attribute *a; Attribute *a;
constraint *c = on->on_bi.bi_private, *cp;
BerVarray b = NULL; BerVarray b = NULL;
int i; int i;
struct berval rsv = BER_BVC("add breaks constraint"); struct berval rsv = BER_BVC("add breaks constraint");
@ -841,6 +856,13 @@ constraint_add( Operation *op, SlapReply *rs )
} }
if ((a = op->ora_e->e_attrs) == NULL) { if ((a = op->ora_e->e_attrs) == NULL) {
if ( ov->allow_empty ) {
/*
* Probably results in an error later on as an empty add makes no
* sense.
*/
return SLAP_CB_CONTINUE;
}
op->o_bd->bd_info = (BackendInfo *)(on->on_info); op->o_bd->bd_info = (BackendInfo *)(on->on_info);
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
"constraint_add: no attrs"); "constraint_add: no attrs");
@ -972,7 +994,8 @@ constraint_update( Operation *op, SlapReply *rs )
{ {
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info; slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
Backend *be = op->o_bd; Backend *be = op->o_bd;
constraint *c = on->on_bi.bi_private, *cp; constraint_info *ov = on->on_bi.bi_private;
constraint *c = ov->constraint, *cp;
Entry *target_entry = NULL, *target_entry_copy = NULL; Entry *target_entry = NULL, *target_entry_copy = NULL;
Modifications *modlist, *m; Modifications *modlist, *m;
BerVarray b = NULL; BerVarray b = NULL;
@ -1002,6 +1025,9 @@ constraint_update( Operation *op, SlapReply *rs )
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n" ); Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_update()\n" );
if ((m = modlist) == NULL) { if ((m = modlist) == NULL) {
if ( ov->allow_empty ) {
return SLAP_CB_CONTINUE;
}
op->o_bd->bd_info = (BackendInfo *)(on->on_info); op->o_bd->bd_info = (BackendInfo *)(on->on_info);
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX, send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
"constraint_update() got null modlist"); "constraint_update() got null modlist");
@ -1209,18 +1235,32 @@ mod_violation:
return (rs->sr_err); return (rs->sr_err);
} }
static int
constraint_init(
BackendDB *be,
ConfigReply *cr )
{
slap_overinst *on = (slap_overinst *) be->bd_info;
on->on_bi.bi_private = ch_calloc( sizeof(constraint_info), 1 );
return 0;
}
static int static int
constraint_destroy( constraint_destroy(
BackendDB *be, BackendDB *be,
ConfigReply *cr ) ConfigReply *cr )
{ {
slap_overinst *on = (slap_overinst *) be->bd_info; slap_overinst *on = (slap_overinst *) be->bd_info;
constraint_info *ov = on->on_bi.bi_private;
constraint *ap, *a2; constraint *ap, *a2;
for ( ap = on->on_bi.bi_private; ap; ap = a2 ) { for ( ap = ov->constraint; ap; ap = a2 ) {
a2 = ap->ap_next; a2 = ap->ap_next;
constraint_free( ap, 1 ); constraint_free( ap, 1 );
} }
ch_free( ov );
return 0; return 0;
} }
@ -1236,6 +1276,7 @@ constraint_initialize( void ) {
constraint_ovl.on_bi.bi_type = "constraint"; constraint_ovl.on_bi.bi_type = "constraint";
constraint_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; constraint_ovl.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
constraint_ovl.on_bi.bi_db_init = constraint_init;
constraint_ovl.on_bi.bi_db_destroy = constraint_destroy; constraint_ovl.on_bi.bi_db_destroy = constraint_destroy;
constraint_ovl.on_bi.bi_op_add = constraint_add; constraint_ovl.on_bi.bi_op_add = constraint_add;
constraint_ovl.on_bi.bi_op_modify = constraint_update; constraint_ovl.on_bi.bi_op_modify = constraint_update;