mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-01-18 11:05:48 +08:00
Added referential integrity and attribute uniqueness overlays
This commit is contained in:
parent
71921f219c
commit
8da6bf194a
53
doc/man/man5/slapo-refint.5
Normal file
53
doc/man/man5/slapo-refint.5
Normal file
@ -0,0 +1,53 @@
|
||||
.TH SLAPO-REFINT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
|
||||
.\" Copyright 2004 The OpenLDAP Foundation All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.\" $OpenLDAP$
|
||||
.SH NAME
|
||||
slapo-refint \- Referential Integrity overlay
|
||||
.SH SYNOPSIS
|
||||
ETCDIR/slapd.conf
|
||||
.SH DESCRIPTION
|
||||
The Referential Integrity overlay can be used with a backend database such as
|
||||
.BR slapd-bdb (5)
|
||||
to maintain the cohesiveness of a schema which utilizes reference attributes.
|
||||
.LP
|
||||
Integrity is maintained by updating database records which contain the named
|
||||
attributes to match the results of a
|
||||
.B modrdn
|
||||
or
|
||||
.B delete
|
||||
operation. For example, if the integrity attribute were configured as
|
||||
.B manager ,
|
||||
deletion of the record "uid=robert,ou=people,o=openldap.org" would trigger a
|
||||
search for all other records which have a
|
||||
.B manager
|
||||
attribute containing that DN. Entries matching that search would have their
|
||||
.B manager
|
||||
attribute removed.
|
||||
.SH CONFIGURATION
|
||||
These
|
||||
.B slapd.conf
|
||||
options apply to the Referential Integrity overlay.
|
||||
They should appear after the
|
||||
.B overlay
|
||||
directive and before any subsequent
|
||||
.B database
|
||||
directive.
|
||||
.TP
|
||||
.B refint_attributes <attribute...>
|
||||
Specify one or more attributes which for which integrity will be maintained
|
||||
as described above.
|
||||
.TP
|
||||
.B refint_nothing <string>
|
||||
Specify an arbitrary value to be used as a placeholder when the last value
|
||||
would otherwise be deleted from an attribute. This can be useful in cases
|
||||
where the schema requires the existence of an attribute for which referential
|
||||
integrity is enforced. The attempted deletion of a required attribute will
|
||||
otherwise result in an Object Class Violation, causing the request to fail.
|
||||
.B
|
||||
.SH FILES
|
||||
.TP
|
||||
ETCDIR/slapd.conf
|
||||
default slapd configuration file
|
||||
.SH SEE ALSO
|
||||
.BR slapd.conf (5).
|
93
doc/man/man5/slapo-unique.5
Normal file
93
doc/man/man5/slapo-unique.5
Normal file
@ -0,0 +1,93 @@
|
||||
.TH SLAPO-UNIQUE 5 "RELEASEDATE" "OpenLDAP LDVERSION"
|
||||
.\" Copyright 2004 The OpenLDAP Foundation All Rights Reserved.
|
||||
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
|
||||
.\" $OpenLDAP$
|
||||
.SH NAME
|
||||
slapo-unique \- Attribute Uniqueness overlay
|
||||
.SH SYNOPSIS
|
||||
ETCDIR/slapd.conf
|
||||
.SH DESCRIPTION
|
||||
The Attribute Uniqueness overlay can be used with a backend database such as
|
||||
.BR slapd-bdb (5)
|
||||
to enforce the uniqueness of some or all attributes within a subtree. This
|
||||
subtree defaults to the base DN of the database for which the Uniqueness
|
||||
overlay is configured.
|
||||
.LP
|
||||
Uniqueness is enforced by searching the subtree to ensure that the values of
|
||||
all attributes presented with an
|
||||
.B add ,
|
||||
.B modify
|
||||
or
|
||||
.B modrdn
|
||||
operation are unique within the subtree.
|
||||
For example, if uniquness were enforced for the
|
||||
.B uid
|
||||
attribute, the subtree would be searched for any other records which also
|
||||
have a
|
||||
.B uid
|
||||
attribute containing the same value. If any are found, the request is
|
||||
rejected.
|
||||
.SH CONFIGURATION
|
||||
These
|
||||
.B slapd.conf
|
||||
options apply to the Attribute Uniqueness overlay.
|
||||
They should appear after the
|
||||
.B overlay
|
||||
directive and before any subsequent
|
||||
.B database
|
||||
directive.
|
||||
.TP
|
||||
.B unique_base <basedn>
|
||||
Configure the subtree against which uniqueness searches will be invoked.
|
||||
The
|
||||
.B basedn
|
||||
defaults to the base DN of the database for which uniqueness is configured.
|
||||
.TP
|
||||
.B unique_ignore <attribute...>
|
||||
Configure one or more attributes for which uniqueness will not be enforced.
|
||||
If not configured, all non-operational (eg, system) attributes must be
|
||||
unique. Note that the
|
||||
.B unique_ignore
|
||||
list should generally contain the
|
||||
.B objectClass ,
|
||||
.B dc ,
|
||||
.B ou
|
||||
and
|
||||
.B o
|
||||
attributes, as these will generally not be unique, nor are they operational
|
||||
attributes.
|
||||
.TP
|
||||
.B unique_attributes <attribute...>
|
||||
Specify one or more attributes which for which uniqueness will be enforced.
|
||||
If not specified, all attributes which are not operational (eg, system
|
||||
attributes such as
|
||||
.B entryUUID )
|
||||
or specified via the
|
||||
.B unique_ignore
|
||||
directive above must be unique within the subtree.
|
||||
.TP
|
||||
.B unique_strict
|
||||
By default, uniqueness is not enforced for null values. Enabling
|
||||
.B unique_strict
|
||||
mode extends the concept of uniqueness to include null values, such that
|
||||
only one attribute within a subtree will be allowed to have a null value.
|
||||
.SH CAVEATS
|
||||
.LP
|
||||
The search key is generated with attributes that are non-operational, not
|
||||
on the
|
||||
.B unique_ignore
|
||||
list, and included in the
|
||||
.B unique_attributes
|
||||
list, in that order. This makes it possible to create interesting and
|
||||
unusable configurations.
|
||||
.LP
|
||||
Typical attributes for the
|
||||
.B unique_ignore
|
||||
directive are intentionally not hardcoded into the overlay to allow for
|
||||
maximum flexibility in meeting site-specific requirements.
|
||||
.SH FILES
|
||||
.TP
|
||||
ETCDIR/slapd.conf
|
||||
default slapd configuration file
|
||||
.SH SEE ALSO
|
||||
.BR slapd.conf (5).
|
@ -19,6 +19,8 @@ SRCS = overlays.c \
|
||||
dyngroup.c \
|
||||
lastmod.c \
|
||||
pcache.c \
|
||||
refint.c \
|
||||
unique.c \
|
||||
rwm.c rwmconf.c rwmdn.c rwmmap.c
|
||||
OBJS = overlays.lo \
|
||||
chain.lo \
|
||||
@ -26,6 +28,8 @@ OBJS = overlays.lo \
|
||||
dyngroup.lo \
|
||||
lastmod.lo \
|
||||
pcache.lo \
|
||||
refint.lo \
|
||||
unique.lo \
|
||||
rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo
|
||||
|
||||
LDAP_INCDIR= ../../../include
|
||||
@ -58,6 +62,12 @@ lastmod.la : lastmod.lo $(@PLAT@_LINK_LIBS)
|
||||
pcache.la : pcache.lo $(@PLAT@_LINK_LIBS)
|
||||
$(LTLINK_MOD) -module -o $@ pcache.lo version.lo $(LINK_LIBS)
|
||||
|
||||
refint.la : refint.lo $(@PLAT@_LINK_LIBS)
|
||||
$(LTLINK_MOD) -module -o $@ refint.lo version.lo $(LINK_LIBS)
|
||||
|
||||
unique.la : unique.lo $(@PLAT@_LINK_LIBS)
|
||||
$(LTLINK_MOD) -module -o $@ unique.lo version.lo $(LINK_LIBS)
|
||||
|
||||
rwm.la : rwm.lo $(@PLAT@_LINK_LIBS)
|
||||
$(LTLINK_MOD) -module -o $@ rwm.lo rwmconf.lo rwmdn.lo rwmmap.lo version.lo $(LINK_LIBS)
|
||||
|
||||
|
675
servers/slapd/overlays/refint.c
Normal file
675
servers/slapd/overlays/refint.c
Normal file
@ -0,0 +1,675 @@
|
||||
/* refint.c - referential integrity module */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2004 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2004 Symas Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
/* ACKNOWLEDGEMENTS:
|
||||
* This work was initially developed by Symas Corp. for inclusion in
|
||||
* OpenLDAP Software. This work was sponsored by Hewlett-Packard.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
/* This module maintains referential integrity for a set of
|
||||
* DN-valued attributes by searching for all references to a given
|
||||
* DN whenever the DN is changed or its entry is deleted, and making
|
||||
* the appropriate update.
|
||||
*
|
||||
* Updates are performed using the database rootdn, but the ModifiersName
|
||||
* is always set to refint_dn.
|
||||
*/
|
||||
|
||||
#ifdef SLAPD_OVER_REFINT
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <ac/string.h>
|
||||
#include <ac/socket.h>
|
||||
|
||||
#include "slap.h"
|
||||
|
||||
static slap_overinst refint;
|
||||
|
||||
/* The DN to use in the ModifiersName for all refint updates */
|
||||
static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
|
||||
|
||||
typedef struct refint_attrs_s {
|
||||
struct refint_attrs_s *next;
|
||||
AttributeDescription *attr;
|
||||
} refint_attrs;
|
||||
|
||||
typedef struct dependents_s {
|
||||
struct dependents_s *next;
|
||||
BerValue dn; /* target dn */
|
||||
Modifications *mm;
|
||||
} dependent_data;
|
||||
|
||||
typedef struct refint_data_s {
|
||||
const char *message; /* breadcrumbs */
|
||||
struct refint_attrs_s *attrs; /* list of known attrs */
|
||||
struct dependents_s *mods; /* modifications returned from callback */
|
||||
BerValue dn; /* basedn in parent, searchdn in call */
|
||||
BerValue newdn; /* replacement value for modrdn callback */
|
||||
BerValue nnewdn; /* normalized replacement value */
|
||||
BerValue nothing; /* the nothing value, if needed */
|
||||
BerValue nnothing; /* normalized nothingness */
|
||||
} refint_data;
|
||||
|
||||
/*
|
||||
** allocate new refint_data;
|
||||
** initialize, copy basedn;
|
||||
** store in on_bi.bi_private;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_db_init(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *)be->bd_info;
|
||||
refint_data *id = ch_malloc(sizeof(refint_data));
|
||||
refint_attrs *ip;
|
||||
id->message = "_init";
|
||||
id->attrs = NULL;
|
||||
id->newdn.bv_val = NULL;
|
||||
id->nothing.bv_val = NULL;
|
||||
id->nnothing.bv_val = NULL;
|
||||
ber_dupbv( &id->dn, &be->be_nsuffix[0] );
|
||||
on->on_bi.bi_private = id;
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** if command = attributes:
|
||||
** foreach argument:
|
||||
** convert to attribute;
|
||||
** add to configured attribute list;
|
||||
** elseif command = basedn:
|
||||
** set our basedn to argument;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_config(
|
||||
BackendDB *be,
|
||||
const char *fname,
|
||||
int lineno,
|
||||
int argc,
|
||||
char **argv
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *) be->bd_info;
|
||||
refint_data *id = on->on_bi.bi_private;
|
||||
refint_attrs *ip;
|
||||
const char *text;
|
||||
AttributeDescription *ad;
|
||||
BerValue dn;
|
||||
int i;
|
||||
|
||||
if(!strcasecmp(*argv, "refint_attributes")) {
|
||||
for(i = 1; i < argc; i++) {
|
||||
for(ip = id->attrs; ip; ip = ip->next)
|
||||
if(!strcmp(argv[i], ip->attr->ad_cname.bv_val)) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: duplicate attribute <s>, ignored\n",
|
||||
fname, lineno, argv[i]);
|
||||
continue;
|
||||
}
|
||||
ad = NULL;
|
||||
if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: bad attribute <%s>, ignored\n",
|
||||
fname, lineno, text);
|
||||
continue; /* XXX */
|
||||
} else if(ad->ad_next) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: multiple attributes match <%s>, ignored\n",
|
||||
fname, lineno, argv[i]);
|
||||
continue;
|
||||
}
|
||||
ip = ch_malloc(sizeof(refint_attrs));
|
||||
ip->attr = ad;
|
||||
ip->next = id->attrs;
|
||||
id->attrs = ip;
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
|
||||
fname, lineno, argv[i]);
|
||||
}
|
||||
} else if(!strcasecmp(*argv, "refint_base")) {
|
||||
/* XXX only one basedn (yet) - need validate argument! */
|
||||
if(id->dn.bv_val) ch_free(id->dn.bv_val);
|
||||
ber_str2bv( argv[1], 0, 0, &dn );
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: new baseDN <%s>\n",
|
||||
fname, lineno, argv[1]);
|
||||
if(dnNormalize(0, NULL, NULL, &dn, &id->dn, NULL)) {
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: bad baseDN!\n", fname, lineno, 0);
|
||||
return(1);
|
||||
}
|
||||
} else if(!strcasecmp(*argv, "refint_nothing")) {
|
||||
if(id->nothing.bv_val) ch_free(id->nothing.bv_val);
|
||||
if(id->nnothing.bv_val) ch_free(id->nnothing.bv_val);
|
||||
ber_str2bv( argv[1], 0, 1, &id->nothing );
|
||||
if(dnNormalize(0, NULL, NULL, &id->nothing, &id->nnothing, NULL)) {
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: bad nothingDN!\n", fname, lineno, 0);
|
||||
return(1);
|
||||
}
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: new nothingDN<%s>\n",
|
||||
fname, lineno, argv[1]);
|
||||
} else {
|
||||
return(SLAP_CONF_UNKNOWN);
|
||||
}
|
||||
|
||||
id->message = "_config";
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** nothing really happens here;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_open(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *)be->bd_info;
|
||||
refint_data *id = on->on_bi.bi_private;
|
||||
id->message = "_open";
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** foreach configured attribute:
|
||||
** free it;
|
||||
** free our basedn;
|
||||
** (do not) free id->message;
|
||||
** reset on_bi.bi_private;
|
||||
** free our config data;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_close(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *) be->bd_info;
|
||||
refint_data *id = on->on_bi.bi_private;
|
||||
refint_attrs *ii, *ij;
|
||||
id->message = "_close";
|
||||
|
||||
for(ii = id->attrs; ii; ii = ij) {
|
||||
ij = ii->next;
|
||||
ch_free(ii);
|
||||
}
|
||||
|
||||
ch_free(id->dn.bv_val);
|
||||
ch_free(id->nothing.bv_val);
|
||||
ch_free(id->nnothing.bv_val);
|
||||
|
||||
on->on_bi.bi_private = NULL; /* XXX */
|
||||
|
||||
ch_free(id);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** delete callback
|
||||
** generates a list of Modification* from search results
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_delete_cb(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Attribute *a;
|
||||
BerVarray b = NULL;
|
||||
refint_data *id, *dd = op->o_callback->sc_private;
|
||||
refint_attrs *ia, *da = dd->attrs;
|
||||
dependent_data *ip, *dp = NULL;
|
||||
Modifications *mp, *ma;
|
||||
int i;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "refint_delete_cb <%s>\n",
|
||||
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
|
||||
|
||||
if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
|
||||
dd->message = "_delete_cb";
|
||||
|
||||
/*
|
||||
** foreach configured attribute type:
|
||||
** if this attr exists in the search result,
|
||||
** and it has a value matching the target:
|
||||
** allocate a Modification;
|
||||
** allocate its array of 2 BerValues;
|
||||
** if only one value, and we have a configured Nothing:
|
||||
** allocate additional Modification
|
||||
** type = MOD_ADD
|
||||
** BerValues[] = { Nothing, NULL };
|
||||
** add to list
|
||||
** type = MOD_DELETE
|
||||
** BerValues[] = { our target dn, NULL };
|
||||
** add this mod to the list of mods;
|
||||
**
|
||||
*/
|
||||
|
||||
ip = ch_malloc(sizeof(dependent_data));
|
||||
ip->dn.bv_val = NULL;
|
||||
ip->next = NULL;
|
||||
ip->mm = NULL;
|
||||
ma = NULL;
|
||||
for(ia = da; ia; ia = ia->next) {
|
||||
if(a = attr_find(rs->sr_entry->e_attrs, ia->attr))
|
||||
for(i = 0, b = a->a_nvals; b[i].bv_val; i++)
|
||||
if(bvmatch(&dd->dn, &b[i])) {
|
||||
if(!ip->dn.bv_val) ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
|
||||
if(!b[1].bv_val && dd->nothing.bv_val) {
|
||||
mp = ch_malloc(sizeof(Modifications));
|
||||
mp->sml_desc = ia->attr; /* XXX */
|
||||
mp->sml_type = a->a_desc->ad_cname;
|
||||
mp->sml_values = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
|
||||
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
|
||||
|
||||
mp->sml_op = LDAP_MOD_ADD;
|
||||
ber_dupbv(&mp->sml_values[0], &dd->nothing);
|
||||
ber_dupbv(&mp->sml_nvalues[0], &dd->nnothing);
|
||||
mp->sml_next = ma;
|
||||
ma = mp;
|
||||
}
|
||||
/* this might violate the object class */
|
||||
mp = ch_malloc(sizeof(Modifications));
|
||||
mp->sml_desc = ia->attr; /* XXX */
|
||||
mp->sml_type = a->a_desc->ad_cname;
|
||||
mp->sml_values = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
|
||||
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
|
||||
mp->sml_op = LDAP_MOD_DELETE;
|
||||
ber_dupbv(&mp->sml_values[0], &dd->dn);
|
||||
ber_dupbv(&mp->sml_nvalues[0], &mp->sml_values[0]);
|
||||
mp->sml_next = ma;
|
||||
ma = mp;
|
||||
Debug(LDAP_DEBUG_TRACE, "refint_delete_cb: %s: %s\n",
|
||||
a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ip->mm = ma;
|
||||
ip->next = dd->mods;
|
||||
dd->mods = ip;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
** null callback
|
||||
** does nothing
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_null_cb(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
((refint_data *)op->o_callback->sc_private)->message = "_null_cb";
|
||||
return(LDAP_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
** modrdn callback
|
||||
** generates a list of Modification* from search results
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_modrdn_cb(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Attribute *a;
|
||||
BerVarray b = NULL;
|
||||
refint_data *id, *dd = op->o_callback->sc_private;
|
||||
refint_attrs *ia, *da = dd->attrs;
|
||||
dependent_data *ip = NULL, *dp = NULL;
|
||||
Modifications *mp;
|
||||
int i, j, fix;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb <%s>\n",
|
||||
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
|
||||
|
||||
if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
|
||||
dd->message = "_modrdn_cb";
|
||||
|
||||
/*
|
||||
** foreach configured attribute type:
|
||||
** if this attr exists in the search result,
|
||||
** and it has a value matching the target:
|
||||
** allocate a pair of Modifications;
|
||||
** make it MOD_ADD the new value and MOD_DELETE the old;
|
||||
** allocate its array of BerValues;
|
||||
** foreach value in the search result:
|
||||
** if it matches our target value, replace it;
|
||||
** otherwise, copy from the search result;
|
||||
** terminate the array of BerValues;
|
||||
** add these mods to the list of mods;
|
||||
**
|
||||
*/
|
||||
|
||||
for(ia = da; ia; ia = ia->next) {
|
||||
if(a = attr_find(rs->sr_entry->e_attrs, ia->attr)) {
|
||||
for(fix = 0, i = 0, b = a->a_nvals; b[i].bv_val; i++)
|
||||
if(bvmatch(&dd->dn, &b[i])) { fix++; break; }
|
||||
if(fix) {
|
||||
if (!ip) {
|
||||
ip = ch_malloc(sizeof(dependent_data));
|
||||
ip->next = NULL;
|
||||
ip->mm = NULL;
|
||||
ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
|
||||
}
|
||||
mp = ch_malloc(sizeof(Modifications));
|
||||
mp->sml_op = LDAP_MOD_ADD;
|
||||
mp->sml_desc = ia->attr; /* XXX */
|
||||
mp->sml_type = ia->attr->ad_cname;
|
||||
mp->sml_values = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
|
||||
ber_dupbv(&mp->sml_values[0], &dd->newdn);
|
||||
ber_dupbv(&mp->sml_nvalues[0], &dd->nnewdn);
|
||||
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
|
||||
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
|
||||
mp->sml_next = ip->mm;
|
||||
ip->mm = mp;
|
||||
mp = ch_malloc(sizeof(Modifications));
|
||||
mp->sml_op = LDAP_MOD_DELETE;
|
||||
mp->sml_desc = ia->attr; /* XXX */
|
||||
mp->sml_type = ia->attr->ad_cname;
|
||||
mp->sml_values = ch_malloc(2 * sizeof(BerValue));
|
||||
mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
|
||||
ber_dupbv(&mp->sml_values[0], &dd->dn);
|
||||
ber_dupbv(&mp->sml_nvalues[0], &dd->dn);
|
||||
mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
|
||||
mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
|
||||
mp->sml_next = ip->mm;
|
||||
ip->mm = mp;
|
||||
Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb: %s: %s\n",
|
||||
a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ip) {
|
||||
ip->next = dd->mods;
|
||||
dd->mods = ip;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** refint_response
|
||||
** search for matching records and modify them
|
||||
*/
|
||||
|
||||
static int
|
||||
refint_response(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Operation nop = *op;
|
||||
SlapReply nrs = { REP_RESULT };
|
||||
slap_callback cb = { NULL, NULL, NULL, NULL };
|
||||
slap_callback cb2 = { NULL, slap_replog_cb, NULL, NULL };
|
||||
slap_callback *cbo, *cbp;
|
||||
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
|
||||
refint_data *id = on->on_bi.bi_private;
|
||||
refint_data dd = *id;
|
||||
refint_attrs *ip;
|
||||
dependent_data *dp;
|
||||
char *fstr, *key, *kp, **dnpp, **ndnpp, *cp;
|
||||
BerValue ndn, moddn, pdn;
|
||||
BerVarray b = NULL;
|
||||
int rc, ac, i, j, ksize;
|
||||
|
||||
id->message = "_refint_response";
|
||||
|
||||
/* If the main op failed or is not a Delete or ModRdn, ignore it */
|
||||
if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
|
||||
rs->sr_err != LDAP_SUCCESS )
|
||||
return SLAP_CB_CONTINUE;
|
||||
|
||||
/*
|
||||
** validate (and count) the list of attrs;
|
||||
**
|
||||
*/
|
||||
|
||||
for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
|
||||
if(!ac) {
|
||||
rs->sr_err = LDAP_OTHER;
|
||||
rs->sr_text = "refint_response called without any attributes";
|
||||
return SLAP_CB_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
** find the backend that matches our configured basedn;
|
||||
** make sure it exists and has search and modify methods;
|
||||
**
|
||||
*/
|
||||
|
||||
nop.o_bd = select_backend(&id->dn, 0, 1);
|
||||
|
||||
if(nop.o_bd) {
|
||||
if (!nop.o_bd->be_search || !nop.o_bd->be_modify) {
|
||||
rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
|
||||
rs->sr_text = "backend missing search and/or modify";
|
||||
return SLAP_CB_CONTINUE;
|
||||
}
|
||||
} else {
|
||||
rs->sr_err = LDAP_OTHER;
|
||||
rs->sr_text = "no known backend? this shouldn't be happening!";
|
||||
return SLAP_CB_CONTINUE;
|
||||
}
|
||||
|
||||
cb2.sc_next = &cb;
|
||||
|
||||
/*
|
||||
** if delete: set delete callback;
|
||||
** else modrdn: create a newdn, set modify callback;
|
||||
**
|
||||
*/
|
||||
|
||||
if(op->o_tag == LDAP_REQ_DELETE) {
|
||||
cb.sc_response = &refint_delete_cb;
|
||||
dd.newdn.bv_val = NULL;
|
||||
dd.nnewdn.bv_val = NULL;
|
||||
} else {
|
||||
cb.sc_response = &refint_modrdn_cb;
|
||||
if ( op->oq_modrdn.rs_newSup ) {
|
||||
pdn = *op->oq_modrdn.rs_newSup;
|
||||
} else {
|
||||
dnParent( &op->o_req_dn, &pdn );
|
||||
}
|
||||
build_new_dn( &dd.newdn, &pdn, &op->orr_newrdn, NULL );
|
||||
if ( op->oq_modrdn.rs_nnewSup ) {
|
||||
pdn = *op->oq_modrdn.rs_nnewSup;
|
||||
} else {
|
||||
dnParent( &op->o_req_ndn, &pdn );
|
||||
}
|
||||
build_new_dn( &dd.nnewdn, &pdn, &op->orr_nnewrdn, NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
** calculate the search key size and allocate it;
|
||||
** build a search filter for all configured attributes;
|
||||
** populate our Operation;
|
||||
** pass our data (attr list, dn) to backend via sc_private;
|
||||
** call the backend search function;
|
||||
** nb: (|(one=thing)) is valid, but do smart formatting anyway;
|
||||
** nb: 16 is arbitrarily a dozen or so extra bytes;
|
||||
**
|
||||
*/
|
||||
|
||||
for(ksize = 16, ip = id->attrs; ip; ip = ip->next)
|
||||
ksize += ip->attr->ad_cname.bv_len + op->o_req_dn.bv_len + 3;
|
||||
kp = key = ch_malloc(ksize);
|
||||
if(--ac) kp += sprintf(key, "(|");
|
||||
for(ip = id->attrs; ip; ip = ip->next)
|
||||
kp += sprintf(kp, "(%s=%s)",
|
||||
ip->attr->ad_cname.bv_val, op->o_req_dn.bv_val);
|
||||
if(ac) *kp++ = ')';
|
||||
*kp = 0;
|
||||
|
||||
nop.ors_filter = str2filter_x(&nop, key);
|
||||
ber_str2bv(key, 0, 0, &nop.ors_filterstr);
|
||||
|
||||
/* callback gets the searched dn instead */
|
||||
dd.dn = op->o_req_ndn;
|
||||
dd.message = "_dependent_search";
|
||||
dd.mods = NULL;
|
||||
cb.sc_private = ⅆ
|
||||
nop.o_callback = &cb;
|
||||
nop.o_tag = LDAP_REQ_SEARCH;
|
||||
nop.ors_scope = LDAP_SCOPE_SUBTREE;
|
||||
nop.ors_deref = LDAP_DEREF_NEVER;
|
||||
nop.ors_slimit = -1;
|
||||
nop.ors_tlimit = -1;
|
||||
nop.o_req_ndn = id->dn;
|
||||
nop.o_req_dn = id->dn;
|
||||
|
||||
/* search */
|
||||
rc = nop.o_bd->be_search(&nop, &nrs);
|
||||
|
||||
filter_free_x(&nop, nop.ors_filter);
|
||||
ch_free(key);
|
||||
ch_free(dd.nnewdn.bv_val);
|
||||
ch_free(dd.newdn.bv_val);
|
||||
dd.newdn.bv_val = NULL;
|
||||
dd.nnewdn.bv_val = NULL;
|
||||
|
||||
if(rc != LDAP_SUCCESS) {
|
||||
rs->sr_err = nrs.sr_err;
|
||||
rs->sr_text = "refint_response search failed";
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* safety? paranoid just in case */
|
||||
if(!cb.sc_private) {
|
||||
rs->sr_err = LDAP_OTHER;
|
||||
rs->sr_text = "whoa! refint_response callback wiped out sc_private?!";
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* presto! now it's a modify request with null callback */
|
||||
cb.sc_response = &refint_null_cb;
|
||||
nop.o_tag = LDAP_REQ_MODIFY;
|
||||
dd.message = "_dependent_modify";
|
||||
|
||||
/* See if the parent operation is going into the replog */
|
||||
cbo = NULL;
|
||||
for (cbp = op->o_callback->sc_next; cbp; cbo=cbp,cbp=cbp->sc_next) {
|
||||
if (cbp->sc_response == slap_replog_cb) {
|
||||
/* Invoke replog now, arrange for our
|
||||
* dependent mods to also be logged
|
||||
*/
|
||||
cbo->sc_next = cbp->sc_next;
|
||||
replog( op );
|
||||
nop.o_callback = &cb2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** [our search callback builds a list of mods]
|
||||
** foreach mod:
|
||||
** make sure its dn has a backend;
|
||||
** connect Modification* chain to our op;
|
||||
** call the backend modify function;
|
||||
** pass any errors upstream;
|
||||
**
|
||||
*/
|
||||
|
||||
for(dp = dd.mods; dp; dp = dp->next) {
|
||||
Modifications **tail, *m;
|
||||
|
||||
for(m = dp->mm; m && m->sml_next; m = m->sml_next);
|
||||
tail = &m->sml_next;
|
||||
nop.o_req_dn = dp->dn;
|
||||
nop.o_req_ndn = dp->dn;
|
||||
nop.o_bd = select_backend(&dp->dn, 0, 1);
|
||||
if(!nop.o_bd) {
|
||||
rs->sr_err = LDAP_OTHER;
|
||||
rs->sr_text = "this should never happen either!";
|
||||
goto done;
|
||||
}
|
||||
nrs.sr_type = REP_RESULT;
|
||||
nop.orm_modlist = dp->mm; /* callback did all the work */
|
||||
nop.o_dn = refint_dn;
|
||||
nop.o_ndn = refint_dn;
|
||||
rs->sr_err = slap_mods_opattrs( &nop, nop.orm_modlist,
|
||||
tail, &rs->sr_text, NULL, 0 );
|
||||
nop.o_dn = nop.o_bd->be_rootdn;
|
||||
nop.o_ndn = nop.o_bd->be_rootndn;
|
||||
if(rs->sr_err != LDAP_SUCCESS) goto done;
|
||||
if((rc = nop.o_bd->be_modify(&nop, &nrs)) != LDAP_SUCCESS) {
|
||||
rs->sr_err = nrs.sr_err;
|
||||
rs->sr_text = "dependent modify failed";
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
for(dp = dd.mods; dp; dp = dd.mods) {
|
||||
dd.mods = dp->next;
|
||||
ch_free(dp->dn.bv_val);
|
||||
slap_mods_free(dp->mm);
|
||||
}
|
||||
dd.mods = NULL;
|
||||
|
||||
return(SLAP_CB_CONTINUE);
|
||||
}
|
||||
|
||||
/*
|
||||
** init_module is last so the symbols resolve "for free" --
|
||||
** it expects to be called automagically during dynamic module initialization
|
||||
*/
|
||||
|
||||
int refint_init() {
|
||||
|
||||
/* statically declared just after the #includes at top */
|
||||
refint.on_bi.bi_type = "refint";
|
||||
refint.on_bi.bi_db_init = refint_db_init;
|
||||
refint.on_bi.bi_db_config = refint_config;
|
||||
refint.on_bi.bi_db_open = refint_open;
|
||||
refint.on_bi.bi_db_close = refint_close;
|
||||
refint.on_response = refint_response;
|
||||
|
||||
return(overlay_register(&refint));
|
||||
}
|
||||
|
||||
#if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
|
||||
int init_module(int argc, char *argv[]) {
|
||||
return refint_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SLAPD_OVER_REFINT */
|
669
servers/slapd/overlays/unique.c
Normal file
669
servers/slapd/overlays/unique.c
Normal file
@ -0,0 +1,669 @@
|
||||
/* unique.c - attribute uniqueness module */
|
||||
/* $OpenLDAP$ */
|
||||
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
*
|
||||
* Copyright 2004 The OpenLDAP Foundation.
|
||||
* Portions Copyright 2004 Symas Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted only as authorized by the OpenLDAP
|
||||
* Public License.
|
||||
*
|
||||
* A copy of this license is available in the file LICENSE in the
|
||||
* top-level directory of the distribution or, alternatively, at
|
||||
* <http://www.OpenLDAP.org/license.html>.
|
||||
*/
|
||||
/* ACKNOWLEDGEMENTS:
|
||||
* This work was initially developed by Symas Corp. for inclusion in
|
||||
* OpenLDAP Software. This work was sponsored by Hewlett-Packard.
|
||||
*/
|
||||
|
||||
#include "portable.h"
|
||||
|
||||
#ifdef SLAPD_OVER_UNIQUE
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <ac/string.h>
|
||||
#include <ac/socket.h>
|
||||
|
||||
#include "slap.h"
|
||||
|
||||
static slap_overinst unique;
|
||||
|
||||
typedef struct unique_attrs_s {
|
||||
struct unique_attrs_s *next; /* list of attrs */
|
||||
AttributeDescription *attr;
|
||||
} unique_attrs;
|
||||
|
||||
typedef struct unique_data_s {
|
||||
const char *message; /* breadcrumbs */
|
||||
struct unique_attrs_s *attrs; /* list of known attrs */
|
||||
struct unique_attrs_s *ignore; /* list of ignored attrs */
|
||||
BerValue dn; /* base of "unique tree" */
|
||||
char strict; /* null considered unique too */
|
||||
} unique_data;
|
||||
|
||||
typedef struct unique_counter_s {
|
||||
int count;
|
||||
} unique_counter;
|
||||
|
||||
/*
|
||||
** allocate new unique_data;
|
||||
** initialize, copy basedn;
|
||||
** store in on_bi.bi_private;
|
||||
**
|
||||
*/
|
||||
|
||||
static int unique_db_init(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *)be->bd_info;
|
||||
unique_data *ud = ch_malloc(sizeof(unique_data));
|
||||
unique_attrs *up;
|
||||
|
||||
/* Debug(LDAP_DEBUG_TRACE, "==> unique_init\n", 0, 0, 0); */
|
||||
|
||||
ud->message = "_init";
|
||||
ud->attrs = NULL;
|
||||
ud->ignore = NULL;
|
||||
ud->strict = 0;
|
||||
|
||||
/* default to the base of our configured database */
|
||||
ber_dupbv(&ud->dn, &be->be_nsuffix[0]);
|
||||
on->on_bi.bi_private = ud;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** if command = attributes:
|
||||
** foreach argument:
|
||||
** convert to attribute;
|
||||
** add to configured attribute list;
|
||||
** elseif command = base:
|
||||
** set our basedn to argument;
|
||||
** else complain about invalid directive;
|
||||
**
|
||||
*/
|
||||
|
||||
static int unique_config(
|
||||
BackendDB *be,
|
||||
const char *fname,
|
||||
int lineno,
|
||||
int argc,
|
||||
char **argv
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *) be->bd_info;
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
unique_attrs *up;
|
||||
const char *text;
|
||||
AttributeDescription *ad;
|
||||
int i;
|
||||
|
||||
ud->message = "_config";
|
||||
Debug(LDAP_DEBUG_TRACE, "==> unique_config\n", 0, 0, 0);
|
||||
|
||||
if(!strcasecmp(*argv, "unique_attributes") ||
|
||||
!strcasecmp(*argv, "unique_ignore")) {
|
||||
for(i = 1; i < argc; i++) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(!strcmp(argv[i], up->attr->ad_cname.bv_val)) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: duplicate attribute <s>, ignored\n",
|
||||
fname, lineno, argv[i]);
|
||||
continue;
|
||||
}
|
||||
ad = NULL;
|
||||
if(slap_str2ad(argv[i], &ad, &text) != LDAP_SUCCESS) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: bad attribute <%s>, ignored\n",
|
||||
fname, lineno, text);
|
||||
continue; /* XXX */
|
||||
} else if(ad->ad_next) {
|
||||
Debug(LDAP_DEBUG_ANY,
|
||||
"%s: line %d: multiple attributes match <%s>, ignored\n",
|
||||
fname, lineno, argv[i]);
|
||||
continue;
|
||||
}
|
||||
up = ch_malloc(sizeof(unique_attrs));
|
||||
up->attr = ad;
|
||||
if(!strcasecmp(*argv, "unique_ignore")) {
|
||||
up->next = ud->ignore;
|
||||
ud->ignore = up;
|
||||
} else {
|
||||
up->next = ud->attrs;
|
||||
ud->attrs = up;
|
||||
}
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: new attribute <%s>\n",
|
||||
fname, lineno, argv[i]);
|
||||
}
|
||||
} else if(!strcasecmp(*argv, "unique_strict")) {
|
||||
ud->strict = 1;
|
||||
} else if(!strcasecmp(*argv, "unique_base")) {
|
||||
struct berval bv;
|
||||
ber_str2bv( argv[1], 0, 0, &bv );
|
||||
ch_free(ud->dn.bv_val);
|
||||
dnNormalize(0, NULL, NULL, &bv, &ud->dn, NULL);
|
||||
Debug(LDAP_DEBUG_ANY, "%s: line %d: new base dn <%s>\n",
|
||||
fname, lineno, argv[1]);
|
||||
} else {
|
||||
return(SLAP_CONF_UNKNOWN);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** mostly, just print the init message;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
unique_open(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *)be->bd_info;
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
ud->message = "_open";
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "unique_open: overlay initialized\n", 0, 0, 0);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** foreach configured attribute:
|
||||
** free it;
|
||||
** free our basedn;
|
||||
** (do not) free ud->message;
|
||||
** reset on_bi.bi_private;
|
||||
** free our config data;
|
||||
**
|
||||
*/
|
||||
|
||||
static int
|
||||
unique_close(
|
||||
BackendDB *be
|
||||
)
|
||||
{
|
||||
slap_overinst *on = (slap_overinst *) be->bd_info;
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
unique_attrs *ii, *ij;
|
||||
ud->message = "_close";
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "==> unique_close\n", 0, 0, 0);
|
||||
|
||||
for(ii = ud->attrs; ii; ii = ij) {
|
||||
ij = ii->next;
|
||||
ch_free(ii);
|
||||
}
|
||||
|
||||
for(ii = ud->ignore; ii; ii = ij) {
|
||||
ij = ii->next;
|
||||
ch_free(ii);
|
||||
}
|
||||
|
||||
ch_free(ud->dn.bv_val);
|
||||
|
||||
on->on_bi.bi_private = NULL; /* XXX */
|
||||
|
||||
ch_free(ud);
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** search callback
|
||||
** if this is a REP_SEARCH, count++;
|
||||
**
|
||||
*/
|
||||
|
||||
static int count_attr_cb(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
/* because you never know */
|
||||
if(!op || !rs) return(0);
|
||||
|
||||
/* Only search entries are interesting */
|
||||
if(rs->sr_type != REP_SEARCH) return(0);
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "==> count_attr_cb <%s>\n",
|
||||
rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
|
||||
|
||||
((unique_counter*)op->o_callback->sc_private)->count++;
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
/* XXX extraneous (slap_response*) to avoid compiler warning */
|
||||
|
||||
static int unique_add(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Operation nop = *op;
|
||||
SlapReply nrs = { REP_RESULT };
|
||||
slap_callback cb = { NULL, NULL, NULL, NULL }; /* XXX */
|
||||
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
|
||||
|
||||
Attribute *a;
|
||||
AttributeDescription *st;
|
||||
BerVarray b = NULL;
|
||||
char *fstr, *key, *kp;
|
||||
const char *why;
|
||||
int i, rc, ks = 16;
|
||||
unique_attrs *up;
|
||||
unique_counter uq = { 0 };
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "==> unique_add <%s>\n", op->o_req_dn.bv_val, 0, 0);
|
||||
|
||||
/* validate backend. Should have already been done, but whatever */
|
||||
nop.o_bd = select_backend(&ud->dn, 0, 1);
|
||||
if(nop.o_bd) {
|
||||
if (!nop.o_bd->be_search) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
|
||||
"backend missing search function");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
} else {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_OTHER,
|
||||
"no known backend? this shouldn't be happening!");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
/*
|
||||
** count everything first;
|
||||
** allocate some memory;
|
||||
** write the search key;
|
||||
**
|
||||
*/
|
||||
|
||||
if(!(a = op->ora_e->e_attrs)) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
|
||||
"unique_add() got null op.ora_e.e_attrs");
|
||||
return(rs->sr_err);
|
||||
} else for(; a; a = a->a_next) {
|
||||
if(is_at_operational(a->a_desc->ad_type)) continue;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(a->a_desc == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(a->a_desc == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
|
||||
ks += b[i].bv_len + a->a_desc->ad_cname.bv_len + 3;
|
||||
else if(ud->strict)
|
||||
ks += a->a_desc->ad_cname.bv_len + 4; /* (attr=*) */
|
||||
}
|
||||
|
||||
key = ch_malloc(ks);
|
||||
|
||||
kp = key + sprintf(key, "(|");
|
||||
|
||||
for(a = op->ora_e->e_attrs; a; a = a->a_next) {
|
||||
if(is_at_operational(a->a_desc->ad_type)) continue;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(a->a_desc == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(a->a_desc == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
if((b = a->a_vals) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
|
||||
kp += sprintf(kp, "(%s=%s)", a->a_desc->ad_cname.bv_val, b[i].bv_val);
|
||||
else if(ud->strict)
|
||||
kp += sprintf(kp, "(%s=*)", a->a_desc->ad_cname.bv_val);
|
||||
}
|
||||
|
||||
kp += sprintf(kp, ")");
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_add %s\n", key, 0, 0);
|
||||
|
||||
nop.ors_filter = str2filter_x(&nop, key);
|
||||
ber_str2bv(key, 0, 0, &nop.ors_filterstr);
|
||||
|
||||
cb.sc_response = (slap_response*)count_attr_cb;
|
||||
cb.sc_private = &uq;
|
||||
nop.o_callback = &cb;
|
||||
nop.o_tag = LDAP_REQ_SEARCH;
|
||||
nop.ors_scope = LDAP_SCOPE_SUBTREE;
|
||||
nop.ors_deref = LDAP_DEREF_NEVER;
|
||||
nop.ors_slimit = -1;
|
||||
nop.ors_tlimit = -1;
|
||||
nop.o_req_ndn = ud->dn;
|
||||
nop.o_ndn = op->o_bd->be_rootndn;
|
||||
|
||||
rc = nop.o_bd->be_search(&nop, &nrs);
|
||||
filter_free_x(&nop, nop.ors_filter);
|
||||
ch_free( key );
|
||||
|
||||
if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, rc, "unique_add search failed");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_add found %d records\n", uq.count, 0, 0);
|
||||
|
||||
if(uq.count) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
|
||||
"some attributes not unique");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
return(SLAP_CB_CONTINUE);
|
||||
}
|
||||
|
||||
|
||||
static int unique_modify(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Operation nop = *op;
|
||||
SlapReply nrs = { REP_RESULT };
|
||||
slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
|
||||
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
|
||||
|
||||
Attribute *a;
|
||||
AttributeDescription *st;
|
||||
BerVarray b = NULL;
|
||||
Modifications *m;
|
||||
char *fstr, *key, *kp;
|
||||
const char *why;
|
||||
int i, rc, ks = 16; /* a handful of extra bytes */
|
||||
unique_attrs *up;
|
||||
unique_counter uq = { 0 };
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "==> unique_modify <%s>\n", op->o_req_dn.bv_val, 0, 0);
|
||||
|
||||
nop.o_bd = select_backend(&ud->dn, 0, 1);
|
||||
if(nop.o_bd) {
|
||||
if (!nop.o_bd->be_search) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
|
||||
"backend missing search function");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
} else {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_OTHER,
|
||||
"no known backend? this shouldn't be happening!");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
/*
|
||||
** count everything first;
|
||||
** allocate some memory;
|
||||
** write the search key;
|
||||
**
|
||||
*/
|
||||
|
||||
if(!(m = op->orm_modlist)) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
|
||||
"unique_modify() got null op.orm_modlist");
|
||||
return(rs->sr_err);
|
||||
} else for(; m; m = m->sml_next) {
|
||||
if(is_at_operational(m->sml_desc->ad_type) ||
|
||||
((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(m->sml_desc == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(m->sml_desc == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
|
||||
ks += b[i].bv_len + m->sml_desc->ad_cname.bv_len + 3;
|
||||
else if(ud->strict)
|
||||
ks += m->sml_desc->ad_cname.bv_len + 4; /* (attr=*) */
|
||||
}
|
||||
|
||||
key = ch_malloc(ks);
|
||||
|
||||
kp = key + sprintf(key, "(|");
|
||||
|
||||
for(m = op->orm_modlist; m; m = m->sml_next) {
|
||||
if(is_at_operational(m->sml_desc->ad_type) ||
|
||||
((m->sml_op & LDAP_MOD_OP) == LDAP_MOD_DELETE)) continue;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(m->sml_desc == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(m->sml_desc == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
if((b = m->sml_values) && b[0].bv_val) for(i = 0; b[i].bv_val; i++)
|
||||
kp += sprintf(kp, "(%s=%s)", m->sml_desc->ad_cname.bv_val, b[i].bv_val);
|
||||
else if(ud->strict)
|
||||
kp += sprintf(kp, "(%s=*)", m->sml_desc->ad_cname.bv_val);
|
||||
}
|
||||
|
||||
kp += sprintf(kp, ")");
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_modify %s\n", key, 0, 0);
|
||||
|
||||
nop.ors_filter = str2filter_x(&nop, key);
|
||||
ber_str2bv(key, 0, 0, &nop.ors_filterstr);
|
||||
|
||||
cb.sc_response = (slap_response*)count_attr_cb;
|
||||
cb.sc_private = &uq;
|
||||
nop.o_callback = &cb;
|
||||
nop.o_tag = LDAP_REQ_SEARCH;
|
||||
nop.ors_scope = LDAP_SCOPE_SUBTREE;
|
||||
nop.ors_deref = LDAP_DEREF_NEVER;
|
||||
nop.ors_slimit = -1;
|
||||
nop.ors_tlimit = -1;
|
||||
nop.o_req_ndn = ud->dn;
|
||||
nop.o_ndn = op->o_bd->be_rootndn;
|
||||
|
||||
rc = nop.o_bd->be_search(&nop, &nrs);
|
||||
ch_free( key );
|
||||
|
||||
if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, rc, "unique_modify search failed");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_modify found %d records\n", uq.count, 0, 0);
|
||||
|
||||
if(uq.count) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
|
||||
"some attributes not unique");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
return(SLAP_CB_CONTINUE);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int unique_modrdn(
|
||||
Operation *op,
|
||||
SlapReply *rs
|
||||
)
|
||||
{
|
||||
Operation nop = *op;
|
||||
SlapReply nrs = { REP_RESULT };
|
||||
slap_callback cb = { NULL, (slap_response*)count_attr_cb, NULL, NULL };
|
||||
slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
|
||||
|
||||
char *fstr, *key, *kp;
|
||||
const char *why;
|
||||
int i, rc, ks = 16; /* a handful of extra bytes */
|
||||
unique_attrs *up;
|
||||
unique_counter uq = { 0 };
|
||||
unique_data *ud = on->on_bi.bi_private;
|
||||
LDAPRDN newrdn;
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "==> unique_modrdn <%s> <%s>\n",
|
||||
op->o_req_dn.bv_val, op->orr_newrdn.bv_val, 0);
|
||||
|
||||
nop.o_bd = select_backend(&ud->dn, 0, 1);
|
||||
if(nop.o_bd) {
|
||||
if (!nop.o_bd->be_search) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_UNWILLING_TO_PERFORM,
|
||||
"backend missing search function");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
} else {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_OTHER,
|
||||
"no known backend? this shouldn't be happening!");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
if(ldap_bv2rdn_x(&op->oq_modrdn.rs_newrdn, &newrdn,
|
||||
(char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx )) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_INVALID_SYNTAX,
|
||||
"unknown type(s) used in RDN");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
for(i = 0; newrdn[i]; i++) {
|
||||
AttributeDescription *ad = NULL;
|
||||
if ( slap_bv2ad( &newrdn[i]->la_attr, &ad, &rs->sr_text )) {
|
||||
ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
|
||||
rs->sr_err = LDAP_INVALID_SYNTAX;
|
||||
send_ldap_result( op, rs );
|
||||
return(rs->sr_err);
|
||||
}
|
||||
newrdn[i]->la_private = ad;
|
||||
}
|
||||
|
||||
for(i = 0; newrdn[i]; i++) {
|
||||
AttributeDescription *ad = newrdn[i]->la_private;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(ad == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(ad == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
ks += newrdn[i]->la_value.bv_len + ad->ad_cname.bv_len + 3;
|
||||
}
|
||||
|
||||
key = ch_malloc(ks);
|
||||
kp = key + sprintf(key, "(|");
|
||||
|
||||
for(i = 0; newrdn[i]; i++) {
|
||||
AttributeDescription *ad = newrdn[i]->la_private;
|
||||
if(ud->ignore) {
|
||||
for(up = ud->ignore; up; up = up->next)
|
||||
if(ad == up->attr) break;
|
||||
if(up) continue;
|
||||
}
|
||||
if(ud->attrs) {
|
||||
for(up = ud->attrs; up; up = up->next)
|
||||
if(ad == up->attr) break;
|
||||
if(!up) continue;
|
||||
}
|
||||
kp += sprintf(kp, "(%s=%s)", ad->ad_cname.bv_val,
|
||||
newrdn[i]->la_value.bv_val);
|
||||
}
|
||||
|
||||
kp += sprintf(kp, ")");
|
||||
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn %s\n", key, 0, 0);
|
||||
|
||||
nop.ors_filter = str2filter_x(&nop, key);
|
||||
ber_str2bv(key, 0, 0, &nop.ors_filterstr);
|
||||
|
||||
cb.sc_response = (slap_response*)count_attr_cb;
|
||||
cb.sc_private = &uq;
|
||||
nop.o_callback = &cb;
|
||||
nop.o_tag = LDAP_REQ_SEARCH;
|
||||
nop.ors_scope = LDAP_SCOPE_SUBTREE;
|
||||
nop.ors_deref = LDAP_DEREF_NEVER;
|
||||
nop.ors_slimit = -1;
|
||||
nop.ors_tlimit = -1;
|
||||
nop.o_req_ndn = ud->dn;
|
||||
nop.o_ndn = op->o_bd->be_rootndn;
|
||||
|
||||
rc = nop.o_bd->be_search(&nop, &nrs);
|
||||
ch_free( key );
|
||||
ldap_rdnfree_x( newrdn, op->o_tmpmemctx );
|
||||
|
||||
if(rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, rc, "unique_modrdn search failed");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
Debug(LDAP_DEBUG_TRACE, "=> unique_modrdn found %d records\n", uq.count, 0, 0);
|
||||
|
||||
if(uq.count) {
|
||||
op->o_bd->bd_info = (BackendInfo *) on->on_info;
|
||||
send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION,
|
||||
"some attributes not unique");
|
||||
return(rs->sr_err);
|
||||
}
|
||||
|
||||
return(SLAP_CB_CONTINUE);
|
||||
}
|
||||
|
||||
/*
|
||||
** init_module is last so the symbols resolve "for free" --
|
||||
** it expects to be called automagically during dynamic module initialization
|
||||
*/
|
||||
|
||||
int unique_init() {
|
||||
|
||||
/* statically declared just after the #includes at top */
|
||||
unique.on_bi.bi_type = "unique";
|
||||
unique.on_bi.bi_db_init = unique_db_init;
|
||||
unique.on_bi.bi_db_config = unique_config;
|
||||
unique.on_bi.bi_db_open = unique_open;
|
||||
unique.on_bi.bi_db_close = unique_close;
|
||||
unique.on_bi.bi_op_add = unique_add;
|
||||
unique.on_bi.bi_op_modify = unique_modify;
|
||||
unique.on_bi.bi_op_modrdn = unique_modrdn;
|
||||
unique.on_bi.bi_op_delete = NULL;
|
||||
|
||||
return(overlay_register(&unique));
|
||||
}
|
||||
|
||||
#if SLAPD_OVER_UNIQUE == SLAPD_MOD_DYNAMIC && defined(PIC)
|
||||
int init_module(int argc, char *argv[]) {
|
||||
return unique_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SLAPD_OVER_UNIQUE */
|
Loading…
Reference in New Issue
Block a user