mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-26 01:29:59 -05:00
add "set" constraint type (ITS#5702)
This commit is contained in:
parent
df725ba475
commit
151d5aec66
2 changed files with 195 additions and 40 deletions
|
|
@ -27,15 +27,16 @@ It should appear after the
|
|||
.B overlay
|
||||
directive.
|
||||
.TP
|
||||
.B constraint_attribute <attribute_name> <type> <value>
|
||||
.B constraint_attribute <attribute_name>[,...] <type> <value>
|
||||
Specifies the constraint which should apply to the attribute named as
|
||||
the first parameter.
|
||||
Two types of constraint are currently supported -
|
||||
.B regex ,
|
||||
.B size ,
|
||||
.B count ,
|
||||
.BR regex ,
|
||||
.BR size ,
|
||||
.BR count ,
|
||||
.BR uri ,
|
||||
and
|
||||
.BR uri .
|
||||
.BR set .
|
||||
|
||||
The parameter following the
|
||||
.B regex
|
||||
|
|
@ -47,6 +48,12 @@ 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
|
||||
type is a string that is interpreted according to the syntax in use
|
||||
for ACL sets. This allows to construct constraints based on the contents
|
||||
of the entry.
|
||||
|
||||
The
|
||||
.B size
|
||||
type can be used to enforce a limit on an attribute length, and the
|
||||
|
|
@ -67,8 +74,11 @@ constraint_attribute userPassword count 3
|
|||
constraint_attribute mail regex ^[:alnum:]+@mydomain.com$
|
||||
constraint_attribute title uri
|
||||
ldap:///dc=catalog,dc=example,dc=com?title?sub?(objectClass=titleCatalog)
|
||||
constraint_attribute cn,sn,givenName set
|
||||
"(this/givenName + [ ] + this/sn) & this/cn"
|
||||
.fi
|
||||
|
||||
.RE
|
||||
A specification like the above would reject any
|
||||
.B mail
|
||||
attribute which did not look like
|
||||
|
|
@ -80,6 +90,13 @@ attribute whose values were not listed in the
|
|||
attribute of any
|
||||
.B titleCatalog
|
||||
entries in the given scope.
|
||||
Finally, it requires the values of the attribute
|
||||
.B cn
|
||||
to be constructed by pairing values of the attributes
|
||||
.B sn
|
||||
and
|
||||
.BR givenName ,
|
||||
separated by a space.
|
||||
.RE
|
||||
.SH FILES
|
||||
.TP
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
|
||||
#define REGEX_STR "regex"
|
||||
#define URI_STR "uri"
|
||||
#define SET_STR "set"
|
||||
#define SIZE_STR "size"
|
||||
#define COUNT_STR "count"
|
||||
|
||||
|
|
@ -54,9 +55,10 @@
|
|||
|
||||
typedef struct constraint {
|
||||
struct constraint *ap_next;
|
||||
AttributeDescription *ap;
|
||||
AttributeDescription **ap;
|
||||
regex_t *re;
|
||||
LDAPURLDesc *lud;
|
||||
int set;
|
||||
size_t size;
|
||||
size_t count;
|
||||
AttributeDescription **attrs;
|
||||
|
|
@ -104,6 +106,8 @@ constraint_free( constraint *cp )
|
|||
ldap_free_urldesc(cp->lud);
|
||||
if (cp->attrs)
|
||||
ch_free(cp->attrs);
|
||||
if (cp->ap)
|
||||
ch_free(cp->ap);
|
||||
ch_free(cp);
|
||||
}
|
||||
|
||||
|
|
@ -122,36 +126,55 @@ constraint_cf_gen( ConfigArgs *c )
|
|||
switch (c->type) {
|
||||
case CONSTRAINT_ATTRIBUTE:
|
||||
for (cp=cn; cp; cp=cp->ap_next) {
|
||||
int len;
|
||||
char *s;
|
||||
char *tstr = NULL;
|
||||
int quotes = 0;
|
||||
int j;
|
||||
|
||||
bv.bv_len = STRLENOF(" ");
|
||||
for (j = 0; cp->ap[j]; j++) {
|
||||
bv.bv_len += cp->ap[j]->ad_cname.bv_len;
|
||||
}
|
||||
|
||||
/* room for commas */
|
||||
bv.bv_len += j - 1;
|
||||
|
||||
len = cp->ap->ad_cname.bv_len + 3;
|
||||
if (cp->re) {
|
||||
len += STRLENOF(REGEX_STR);
|
||||
tstr = REGEX_STR;
|
||||
} else if (cp->lud) {
|
||||
len += STRLENOF(URI_STR);
|
||||
tstr = URI_STR;
|
||||
} else if (cp->set) {
|
||||
tstr = SET_STR;
|
||||
quotes = 1;
|
||||
} else if (cp->size) {
|
||||
len += STRLENOF(SIZE_STR);
|
||||
tstr = SIZE_STR;
|
||||
} else if (cp->count) {
|
||||
len += STRLENOF(COUNT_STR);
|
||||
tstr = COUNT_STR;
|
||||
}
|
||||
len += cp->val.bv_len;
|
||||
|
||||
s = ch_malloc(len);
|
||||
bv.bv_len += strlen(tstr);
|
||||
bv.bv_len += cp->val.bv_len + 2*quotes;
|
||||
|
||||
s = bv.bv_val = ch_malloc(bv.bv_len + 1);
|
||||
|
||||
s = lutil_strncopy( s, cp->ap[0]->ad_cname.bv_val, cp->ap[0]->ad_cname.bv_len );
|
||||
for (j = 1; cp->ap[j]; j++) {
|
||||
*s++ = ',';
|
||||
s = lutil_strncopy( s, cp->ap[j]->ad_cname.bv_val, cp->ap[j]->ad_cname.bv_len );
|
||||
}
|
||||
*s++ = ' ';
|
||||
s = lutil_strcopy( s, tstr );
|
||||
*s++ = ' ';
|
||||
if ( quotes ) *s++ = '"';
|
||||
s = lutil_strncopy( s, cp->val.bv_val, cp->val.bv_len );
|
||||
if ( quotes ) *s++ = '"';
|
||||
*s = '\0';
|
||||
|
||||
bv.bv_len = snprintf(s, len, "%s %s %s", cp->ap->ad_cname.bv_val,
|
||||
tstr, cp->val.bv_val);
|
||||
bv.bv_val = s;
|
||||
rc = value_add_one( &c->rvalue_vals, &bv );
|
||||
if (rc == LDAP_SUCCESS)
|
||||
rc = value_add_one( &c->rvalue_nvals, &bv );
|
||||
ch_free(bv.bv_val);
|
||||
if (rc) return rc;
|
||||
rc = value_add_one( &c->rvalue_nvals, &bv );
|
||||
if (rc) return rc;
|
||||
ch_free(s);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
@ -198,14 +221,24 @@ constraint_cf_gen( ConfigArgs *c )
|
|||
case SLAP_CONFIG_ADD:
|
||||
case LDAP_MOD_ADD:
|
||||
switch (c->type) {
|
||||
case CONSTRAINT_ATTRIBUTE:
|
||||
if ( slap_str2ad( c->argv[1], &ap.ap, &text ) ) {
|
||||
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
||||
"%s <%s>: %s\n", c->argv[0], c->argv[1], text );
|
||||
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
|
||||
"%s: %s\n", c->log, c->cr_msg, 0 );
|
||||
return( ARG_BAD_CONF );
|
||||
case CONSTRAINT_ATTRIBUTE: {
|
||||
int j;
|
||||
char **attrs = ldap_str2charray( c->argv[1], "," );
|
||||
|
||||
for ( j = 0; attrs[j]; j++)
|
||||
/* just count */ ;
|
||||
ap.ap = ch_calloc( sizeof(AttributeDescription*), j + 1 );
|
||||
for ( j = 0; attrs[j]; j++) {
|
||||
if ( slap_str2ad( attrs[j], &ap.ap[j], &text ) ) {
|
||||
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
||||
"%s <%s>: %s\n", c->argv[0], attrs[j], text );
|
||||
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE,
|
||||
"%s: %s\n", c->log, c->cr_msg, 0 );
|
||||
ldap_memvfree((void**)attrs);
|
||||
return( ARG_BAD_CONF );
|
||||
}
|
||||
}
|
||||
ldap_memvfree((void**)attrs);
|
||||
|
||||
if ( strcasecmp( c->argv[2], REGEX_STR ) == 0) {
|
||||
int err;
|
||||
|
|
@ -294,6 +327,11 @@ constraint_cf_gen( ConfigArgs *c )
|
|||
}
|
||||
|
||||
ber_str2bv( c->argv[3], 0, 1, &ap.val );
|
||||
|
||||
} else if ( strcasecmp( c->argv[2], SET_STR ) == 0 ) {
|
||||
ap.set = 1;
|
||||
ber_str2bv( c->argv[3], 0, 1, &ap.val );
|
||||
|
||||
} else {
|
||||
snprintf( c->cr_msg, sizeof( c->cr_msg ),
|
||||
"%s %s: Unknown constraint type: %s",
|
||||
|
|
@ -309,6 +347,7 @@ constraint_cf_gen( ConfigArgs *c )
|
|||
a2->re = ap.re;
|
||||
a2->val = ap.val;
|
||||
a2->lud = ap.lud;
|
||||
a2->set = ap.set;
|
||||
a2->size = ap.size;
|
||||
a2->count = ap.count;
|
||||
if ( a2->lud ) {
|
||||
|
|
@ -317,7 +356,7 @@ constraint_cf_gen( ConfigArgs *c )
|
|||
}
|
||||
a2->attrs = ap.attrs;
|
||||
on->on_bi.bi_private = a2;
|
||||
break;
|
||||
} break;
|
||||
default:
|
||||
abort();
|
||||
break;
|
||||
|
|
@ -468,7 +507,7 @@ constraint_violation( constraint *c, struct berval *bv, Operation *op, SlapReply
|
|||
return LDAP_CONSTRAINT_VIOLATION; /* constraint violation */
|
||||
|
||||
}
|
||||
|
||||
|
||||
return LDAP_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -518,7 +557,11 @@ constraint_add( Operation *op, SlapReply *rs )
|
|||
if (is_at_operational(a->a_desc->ad_type)) continue;
|
||||
|
||||
for(cp = c; cp; cp = cp->ap_next) {
|
||||
if (cp->ap != a->a_desc) continue;
|
||||
int j;
|
||||
for (j = 0; cp->ap[j]; j++) {
|
||||
if (cp->ap[j] == a->a_desc) break;
|
||||
}
|
||||
if (cp->ap[j] == NULL) continue;
|
||||
if ((b = a->a_vals) == NULL) continue;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE,
|
||||
|
|
@ -537,8 +580,15 @@ constraint_add( Operation *op, SlapReply *rs )
|
|||
goto add_violation;
|
||||
}
|
||||
}
|
||||
|
||||
if (cp->set && acl_match_set(&cp->val, op, op->ora_e, NULL) == 0) {
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto add_violation; /* constraint violation */
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Default is to just fall through to the normal processing */
|
||||
return SLAP_CB_CONTINUE;
|
||||
|
||||
|
|
@ -559,7 +609,7 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
|
||||
Backend *be = op->o_bd;
|
||||
constraint *c = on->on_bi.bi_private, *cp;
|
||||
Entry *target_entry = NULL;
|
||||
Entry *target_entry = NULL, *target_entry_copy = NULL;
|
||||
Modifications *m;
|
||||
BerVarray b = NULL;
|
||||
int i;
|
||||
|
|
@ -567,7 +617,7 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
int rc;
|
||||
char *msg = NULL;
|
||||
|
||||
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_modify()", 0,0,0);
|
||||
Debug( LDAP_DEBUG_CONFIG|LDAP_DEBUG_NONE, "constraint_modify()\n", 0,0,0);
|
||||
if ((m = op->orm_modlist) == NULL) {
|
||||
op->o_bd->bd_info = (BackendInfo *)(on->on_info);
|
||||
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
|
||||
|
|
@ -577,8 +627,7 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
|
||||
/* Do we need to count attributes? */
|
||||
for(cp = c; cp; cp = cp->ap_next) {
|
||||
if (cp->count != 0) {
|
||||
|
||||
if (cp->count != 0 || cp->set) {
|
||||
op->o_bd = on->on_info->oi_origdb;
|
||||
rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &target_entry );
|
||||
op->o_bd = be;
|
||||
|
|
@ -600,11 +649,8 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
for(;m; m = m->sml_next) {
|
||||
int ce = 0;
|
||||
|
||||
/* Get this attribute count, if needed */
|
||||
if (target_entry)
|
||||
ce = constraint_count_attr(target_entry, m->sml_desc);
|
||||
|
||||
if (is_at_operational( m->sml_desc->ad_type )) continue;
|
||||
|
||||
if ((( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_ADD) &&
|
||||
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_REPLACE) &&
|
||||
(( m->sml_op & LDAP_MOD_OP ) != LDAP_MOD_DELETE))
|
||||
|
|
@ -614,8 +660,18 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
if ((( b = m->sml_values ) == NULL ) || (b[0].bv_val == NULL))
|
||||
continue;
|
||||
|
||||
/* Get this attribute count, if needed */
|
||||
if (target_entry)
|
||||
ce = constraint_count_attr(target_entry, m->sml_desc);
|
||||
|
||||
for(cp = c; cp; cp = cp->ap_next) {
|
||||
if (cp->ap != m->sml_desc) continue;
|
||||
int j;
|
||||
for (j = 0; cp->ap[j]; j++) {
|
||||
if (cp->ap[j] == m->sml_desc) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cp->ap[j] == NULL) continue;
|
||||
|
||||
if (cp->count != 0) {
|
||||
int ca;
|
||||
|
|
@ -655,14 +711,91 @@ constraint_modify( Operation *op, SlapReply *rs )
|
|||
goto mod_violation;
|
||||
}
|
||||
}
|
||||
|
||||
if (cp->set && target_entry) {
|
||||
if (target_entry_copy == NULL) {
|
||||
Modifications *ml;
|
||||
|
||||
target_entry_copy = entry_dup(target_entry);
|
||||
|
||||
/* apply modifications, in an attempt
|
||||
* to estimate what the entry would
|
||||
* look like in case all modifications
|
||||
* pass */
|
||||
for ( ml = op->orm_modlist; ml; ml = ml->sml_next ) {
|
||||
Modification *mod = &ml->sml_mod;
|
||||
const char *text;
|
||||
char textbuf[SLAP_TEXT_BUFLEN];
|
||||
size_t textlen = sizeof(textbuf);
|
||||
int err;
|
||||
|
||||
switch ( mod->sm_op ) {
|
||||
case LDAP_MOD_ADD:
|
||||
err = modify_add_values( target_entry_copy,
|
||||
mod, get_permissiveModify(op),
|
||||
&text, textbuf, textlen );
|
||||
break;
|
||||
|
||||
case LDAP_MOD_DELETE:
|
||||
err = modify_delete_values( target_entry_copy,
|
||||
mod, get_permissiveModify(op),
|
||||
&text, textbuf, textlen );
|
||||
break;
|
||||
|
||||
case LDAP_MOD_REPLACE:
|
||||
err = modify_replace_values( target_entry_copy,
|
||||
mod, get_permissiveModify(op),
|
||||
&text, textbuf, textlen );
|
||||
break;
|
||||
|
||||
case LDAP_MOD_INCREMENT:
|
||||
err = modify_increment_values( target_entry_copy,
|
||||
mod, get_permissiveModify(op),
|
||||
&text, textbuf, textlen );
|
||||
break;
|
||||
|
||||
case SLAP_MOD_SOFTADD:
|
||||
mod->sm_op = LDAP_MOD_ADD;
|
||||
err = modify_add_values( target_entry_copy,
|
||||
mod, get_permissiveModify(op),
|
||||
&text, textbuf, textlen );
|
||||
mod->sm_op = SLAP_MOD_SOFTADD;
|
||||
if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
|
||||
err = LDAP_SUCCESS;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err = LDAP_OTHER;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( err != LDAP_SUCCESS ) {
|
||||
rc = err;
|
||||
goto mod_violation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( acl_match_set(&cp->val, op, target_entry_copy, NULL) == 0) {
|
||||
rc = LDAP_CONSTRAINT_VIOLATION;
|
||||
goto mod_violation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (target_entry) {
|
||||
op->o_bd = on->on_info->oi_origdb;
|
||||
be_entry_release_r(op, target_entry);
|
||||
op->o_bd = be;
|
||||
}
|
||||
|
||||
if (target_entry_copy) {
|
||||
entry_free(target_entry_copy);
|
||||
}
|
||||
|
||||
return SLAP_CB_CONTINUE;
|
||||
|
||||
mod_violation:
|
||||
|
|
@ -672,6 +805,11 @@ mod_violation:
|
|||
be_entry_release_r(op, target_entry);
|
||||
op->o_bd = be;
|
||||
}
|
||||
|
||||
if (target_entry_copy) {
|
||||
entry_free(target_entry_copy);
|
||||
}
|
||||
|
||||
op->o_bd->bd_info = (BackendInfo *)(on->on_info);
|
||||
if ( rc == LDAP_CONSTRAINT_VIOLATION ) {
|
||||
msg = print_message( &rsv, m->sml_desc );
|
||||
|
|
|
|||
Loading…
Reference in a new issue