r9899: Be more conservative about what is sent to the remote server in ldb_map.
authorJelmer Vernooij <jelmer@samba.org>
Thu, 1 Sep 2005 15:33:31 +0000 (15:33 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:36:21 +0000 (13:36 -0500)
source/dsdb/samdb/ldb_modules/samba3sam.c
source/lib/ldb/modules/ldb_map.c
source/lib/ldb/modules/ldb_map.h
source/scripting/libjs/upgrade.js
testdata/samba3/samba3.ldif
testprogs/ejs/samba3sam

index 9337b61..9730363 100644 (file)
@@ -85,7 +85,7 @@ static struct ldb_val convert_unix_name2id(struct ldb_module *module, TALLOC_CTX
 static struct ldb_val encode_sid(struct ldb_module *module, TALLOC_CTX *ctx, const struct ldb_val *val)
 {
        struct dom_sid *sid = dom_sid_parse_talloc(ctx, (char *)val->data);
-       struct ldb_val *out = talloc_zero(out, struct ldb_val);
+       struct ldb_val *out = talloc_zero(ctx, struct ldb_val);
        NTSTATUS status;
 
        if (sid == NULL) {
@@ -128,9 +128,34 @@ static struct ldb_val decode_sid(struct ldb_module *module, TALLOC_CTX *ctx, con
 }
 
 const struct ldb_map_objectclass samba3_objectclasses[] = {
-       { "group", "sambaGroupMapping" },
-       { "user", "sambaSAMAccount" },
-       { "domain", "sambaDomain" },
+       { 
+               .local_name = "group", 
+               .remote_name = "sambaGroupMapping",
+               .musts = { "gidNumber", "sambaSID", "sambaGroupType", NULL },
+               .mays = { "displayName", "description", "sambaSIDList", NULL },
+       },
+       { 
+               .local_name = "user", 
+               .remote_name = "sambaSAMAccount",
+               .base_classes = { "top", NULL },
+               .musts = { "uid", "sambaSID", NULL },
+               .mays = { "cn", "sambaLMPassword", "sambaNTPassword",
+                       "sambaPwdLastSet", "sambaLogonTime", "sambaLogoffTime",
+                       "sambaKickoffTime", "sambaPwdCanChange", "sambaPwdMustChange",
+                       "sambaAcctFlags", "displayName", "sambaHomePath", "sambaHomeDrive",
+                       "sambaLogonScript", "sambaProfilePath", "description", "sambaUserWorkstations",
+                       "sambaPrimaryGroupSID", "sambaDomainName", "sambaMungedDial",
+                       "sambaBadPasswordCount", "sambaBadPasswordTime",
+               "sambaPasswordHistory", "sambaLogonHours", NULL }
+       
+       },
+       { 
+               .local_name = "domain", 
+               .remote_name = "sambaDomain",
+               .base_classes = { "top", NULL },
+               .musts = { "sambaDomainName", "sambaSID", NULL },
+               .mays = { "sambaNextRid", "sambaNextGroupRid", "sambaNextUserRid", "sambaAlgorithmicRidBase", NULL },
+       },
        { NULL, NULL }
 };
 
@@ -237,17 +262,6 @@ const struct ldb_map_attribute samba3_attributes[] =
                },
        },
 
-       /* gidNumber -> unixName */
-       {
-               .local_name = "unixName",
-               .type = MAP_CONVERT,
-               .u = {
-                       .convert = {
-                               .remote_name = "gidNumber",
-                       },
-               },
-       },
-
        /* uid -> unixName */
        {
                .local_name = "unixName",
index 5b1afb5..5a5575d 100644 (file)
 #include "ldb/include/ldb_private.h"
 #include "ldb/modules/ldb_map.h"
 
-/* 
- *  - map_message_outgoing() should:
- *   - modify: not worry about anything simply map and hope everything 
- *     will be ok.
- *   - make a list of remote objectclasses that will be used 
- *     given the attributes that are available
- *   - only add attribute to the remote message if 
- *     it is allowed by the objectclass
- *
+/* FIXME:
+ *  - per remote objectclass: 
+ *     - remote name
+ *     - local name
+ *     - remote bases
+ *     - musts
+ *     - mays
  */
 
 /*
@@ -95,6 +93,17 @@ static const struct ldb_map_attribute builtin_attribute_maps[] = {
        }
 };
 
+static const struct ldb_map_objectclass *map_find_objectclass_remote(struct ldb_map_context *privdat, const char *name)
+{
+       int i;
+       for (i = 0; privdat->objectclass_maps[i].remote_name; i++) {
+               if (!ldb_attr_cmp(privdat->objectclass_maps[i].remote_name, name))
+                       return &privdat->objectclass_maps[i];
+       }
+
+       return NULL;
+}
+
 struct map_private {
        struct ldb_map_context context;
        const char *last_err_string;
@@ -105,41 +114,39 @@ static struct ldb_map_context *map_get_privdat(struct ldb_module *module)
        return &((struct map_private *)module->private_data)->context;
 }
 
-static const struct ldb_map_objectclass *map_find_objectclass_local(struct ldb_map_context *privdat, const char *name)
-{
-       int i;
-       for (i = 0; privdat->objectclass_maps[i].local_name; i++) {
-               if (!ldb_attr_cmp(privdat->objectclass_maps[i].local_name, name))
-                       return &privdat->objectclass_maps[i];
-       }
-
-       return NULL;
-}
-
-/* Decide whether a add/modify should be pushed to the 
- * remote LDAP server. We currently only do this if we see an objectClass we know */
-static int map_is_mappable(struct ldb_map_context *privdat, const struct ldb_message *msg)
+/* Check whether the given attribute can fit into the specified 
+ * message, obeying objectClass restrictions */
+static int map_msg_valid_attr(struct ldb_module *module, const struct ldb_message *msg, const char *attr)
 {
-       int i;
-       struct ldb_message_element *el;
-
-       if (ldb_dn_is_special(msg->dn))
-               return 0;
-       
-       el = ldb_msg_find_element(msg, "objectClass");
+       struct ldb_map_context *map = module->private_data;
+       int i, j;
+       struct ldb_message_element *el = ldb_msg_find_element(msg, "objectClass");
 
-       /* No objectClass... */
        if (el == NULL) {
+               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "Can't find objectClass");
                return 0;
        }
 
        for (i = 0; i < el->num_values; i++) {
-               if (map_find_objectclass_local(privdat, (char *)el->values[i].data))
-                       return 1;
+               const struct ldb_map_objectclass *class = map_find_objectclass_remote(map, (char *)el->values[i].data);
+
+               if (!class) 
+                       continue;
+               
+               for (j = 0; class->musts[j]; j++) {
+                       if (!ldb_attr_cmp(class->musts[j], attr))
+                               return 1;
+               }
+
+               for (j = 0; class->mays[j]; j++) {
+                       if (!ldb_attr_cmp(class->mays[j], attr))
+                               return 1;
+               }
        }
 
        return 0;
-}
+} 
+
 
 /* find an attribute by the local name */
 static const struct ldb_map_attribute *map_find_attr_local(struct ldb_map_context *privdat, const char *attr)
@@ -154,6 +161,40 @@ static const struct ldb_map_attribute *map_find_attr_local(struct ldb_map_contex
        return NULL;
 }
 
+/* Check if a given attribute can be created by doing mapping from a local attribute to a remote one */
+static int map_msg_can_map_attr(struct ldb_module *module, const struct ldb_message *msg, const char *attr_name)
+{
+       struct ldb_map_context *map = module->private_data;
+       int i, j;
+       
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_map_attribute *attr = map_find_attr_local(map, msg->elements[i].name);
+
+               if (!attr) 
+                       continue;
+
+               switch (attr->type) {
+               case MAP_IGNORE: continue;
+               case MAP_KEEP:
+                               if (ldb_attr_cmp(attr->local_name, attr_name) == 0) return 1;
+                               break;
+               case MAP_RENAME:
+               case MAP_CONVERT:
+                               if (ldb_attr_cmp(attr->u.rename.remote_name, attr_name) == 0) return 1;
+                               break;
+               case MAP_GENERATE:
+                               for (j = 0; attr->u.generate.remote_names[j]; j++) {
+                                       if (ldb_attr_cmp(attr->u.generate.remote_names[j], attr_name) == 0) return 1;
+                               }
+                               break;
+               }
+       }
+
+       return 0;
+}
+
+
+
 /* find an attribute by the remote name */
 static const struct ldb_map_attribute *map_find_attr_remote(struct ldb_map_context *privdat, const char *attr)
 {
@@ -547,8 +588,6 @@ static struct ldb_message *ldb_map_message_incoming(struct ldb_module *module, c
 
        msg->dn = map_remote_dn(module, module, mi->dn);
 
-       ldb_msg_add_string(module->ldb, msg, "mappedFromDn", ldb_dn_linearize(msg, mi->dn));
-
        /* Loop over attrs, find in ldb_map_attribute array and 
         * run generate() */
 
@@ -638,94 +677,6 @@ static struct ldb_message *ldb_map_message_incoming(struct ldb_module *module, c
        return msg;
 }
 
-/* Used for add, modify */
-static int ldb_map_message_outgoing(struct ldb_module *module, const struct ldb_message *mo, struct ldb_message **fb, struct ldb_message **mp)
-{
-       struct ldb_map_context *privdat = map_get_privdat(module);
-       struct ldb_message_element *elm;
-       int i,j;
-
-       *fb = talloc_zero(module, struct ldb_message);
-       (*fb)->dn = talloc_reference(*fb, mo->dn);
-
-       *mp = talloc_zero(module, struct ldb_message);
-       (*mp)->dn = map_local_dn(module, module, mo->dn);
-
-       /* Loop over mi and call generate_remote for each attribute */
-       for (i = 0; i < mo->num_elements; i++) {
-               const struct ldb_map_attribute *attr = map_find_attr_local(privdat, mo->elements[i].name);
-               enum ldb_map_attr_type map_type;
-
-               if (!attr) {
-                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Undefined local attribute '%s', ignoring\n", mo->elements[i].name);
-                       map_type = MAP_IGNORE;
-                       continue;
-               } else map_type = attr->type;
-
-               switch (map_type) {
-               case MAP_IGNORE: /* Add to fallback message */
-                       elm = talloc(*fb, struct ldb_message_element);
-
-                       elm->num_values = mo->elements[i].num_values;
-                       elm->values = talloc_reference(elm, mo->elements[i].values);
-                       elm->name = talloc_strdup(elm, mo->elements[i].name);
-                       
-                       ldb_msg_add(module->ldb, *fb, elm, mo->elements[i].flags);      
-                       break;
-               case MAP_RENAME:
-                       elm = talloc(*mp, struct ldb_message_element);
-
-                       elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
-                       elm->num_values = mo->elements[i].num_values;
-                       elm->values = talloc_reference(elm, mo->elements[i].values);
-
-                       ldb_msg_add(module->ldb, *mp, elm, mo->elements[i].flags);
-                       break;
-
-               case MAP_CONVERT:
-                       elm = talloc(*mp, struct ldb_message_element);
-
-                       elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
-                       elm->num_values = mo->elements[i].num_values;
-                       elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
-                       
-                       for (j = 0; j < elm->num_values; j++) {
-                               elm->values[j] = attr->u.convert.convert_local(module, *mp, &mo->elements[i].values[j]);
-                       }
-
-                       ldb_msg_add(module->ldb, *mp, elm, mo->elements[i].flags);
-                       break;
-
-               case MAP_KEEP:
-                       elm = talloc(*mp, struct ldb_message_element);
-
-                       elm->num_values = mo->elements[i].num_values;
-                       elm->values = talloc_reference(elm, mo->elements[i].values);
-                       elm->name = talloc_strdup(elm, mo->elements[i].name);
-                       
-                       ldb_msg_add(module->ldb, *mp, elm, mo->elements[i].flags);      
-                       break;
-
-               case MAP_GENERATE:
-                       attr->u.generate.generate_remote(module, attr->local_name, mo, *mp);
-                       break;
-               } 
-       }
-
-       if ((*fb)->num_elements == 0) {
-               ldb_msg_add_string(module->ldb, *fb, "isMapped", "TRUE");
-       }
-
-       if ((*mp)->num_elements == 0) {
-               /* No elements, discard.. */
-               talloc_free(*mp);
-               *mp = NULL;
-       }
-
-       return 0;
-}
-
-
 /*
   rename a record
 */
@@ -734,6 +685,13 @@ static int map_rename(struct ldb_module *module, const struct ldb_dn *olddn, con
        struct ldb_map_context *privdat = map_get_privdat(module);
        struct ldb_dn *n_olddn, *n_newdn;
        int fb_ret, mp_ret;
+
+       fb_ret = ldb_next_rename_record(module, olddn, newdn);
+       
+       if (fb_ret != -1) {
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Fallback record renamed");
+               return fb_ret;
+       }
        
        n_olddn = map_local_dn(module, module, olddn);
        n_newdn = map_local_dn(module, module, newdn);
@@ -743,16 +701,12 @@ static int map_rename(struct ldb_module *module, const struct ldb_dn *olddn, con
                ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Mapped record renamed");
        }
 
-       fb_ret = ldb_next_rename_record(module, olddn, newdn);
+       ldb_next_rename_record(module, olddn, newdn);
        
-       if (fb_ret != -1) {
-               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Fallback record renamed");
-       }
-
        talloc_free(n_olddn);
        talloc_free(n_newdn);
        
-       return (fb_ret == -1 && mp_ret == -1)?-1:0;
+       return mp_ret;
 }
 
 /*
@@ -764,6 +718,12 @@ static int map_delete(struct ldb_module *module, const struct ldb_dn *dn)
        struct ldb_dn *newdn;
        int fb_ret, mp_ret;
 
+       fb_ret = ldb_next_delete_record(module, dn);
+       if (fb_ret != -1) {
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Fallback record deleted");
+               return fb_ret;
+       }
+
        newdn = map_local_dn(module, module, dn);
 
        mp_ret = ldb_delete(privdat->mapped_ldb, newdn);
@@ -771,14 +731,11 @@ static int map_delete(struct ldb_module *module, const struct ldb_dn *dn)
                ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Mapped record deleted");
        }
 
-       fb_ret = ldb_next_delete_record(module, dn);
-       if (fb_ret != -1) {
-               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Fallback record deleted");
-       }
+       ldb_next_delete_record(module, newdn);
 
        talloc_free(newdn);
 
-       return (fb_ret == -1 && mp_ret == -1)?-1:0;
+       return mp_ret;
 }
 
 /* search fallback database */
@@ -869,7 +826,7 @@ static int map_search_bytree_mp(struct ldb_module *module, const struct ldb_dn *
 
                merged = ldb_map_message_incoming(module, attrs, newres[i]);
                
-               /* Merge with additional data from local database */
+               /* Merge with additional data from fallback database */
                extraret = ldb_next_search(module, merged->dn, LDB_SCOPE_BASE, "", NULL, &extrares);
 
                if (extraret == -1) {
@@ -888,10 +845,6 @@ static int map_search_bytree_mp(struct ldb_module *module, const struct ldb_dn *
                        for (j = 0; j < extrares[0]->num_elements; j++) {
                                ldb_msg_add(module->ldb, merged, &(extrares[0]->elements[j]), extrares[0]->elements[j].flags);
                        }
-
-                       ldb_msg_add_string(module->ldb, merged, "extraMapped", "TRUE");
-               } else {
-                       ldb_msg_add_string(module->ldb, merged, "extraMapped", "FALSE");
                }
                
                if (ldb_match_msg(module->ldb, merged, tree, base, scope) != 0) {
@@ -966,6 +919,20 @@ static int map_search(struct ldb_module *module, const struct ldb_dn *base,
        return ret;
 }
 
+static int msg_contains_objectclass(const struct ldb_message *msg, const char *name)
+{
+       struct ldb_message_element *el = ldb_msg_find_element(msg, "objectClass");
+       int i;
+       
+       for (i = 0; i < el->num_values; i++) {
+               if (ldb_attr_cmp((char *)el->values[i].data, name) == 0) {
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
 /*
   add a record
 */
@@ -974,32 +941,159 @@ static int map_add(struct ldb_module *module, const struct ldb_message *msg)
        int ret;
        struct ldb_map_context *privdat = map_get_privdat(module);
        struct ldb_message *fb, *mp;
+       struct ldb_message_element *ocs;
+       int i;
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_map_add");
 
-       if (!map_is_mappable(privdat, msg)) {
+       if (ldb_dn_is_special(msg->dn)) {
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_map_add: Added fallback record");
                return ldb_next_add_record(module, msg);
        }
 
-       if (ldb_map_message_outgoing(module, msg, &fb, &mp) == -1)
-               return -1;
+       mp = talloc_zero(module, struct ldb_message);
+       mp->dn = map_local_dn(module, mp, msg->dn);
 
-       if (fb != NULL) {
-               ret = ldb_next_add_record(module, fb);
-               if (ret == -1) {
-                       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Adding fallback record failed");
-                       return -1;
+       fb = talloc_zero(module, struct ldb_message);
+       fb->dn = talloc_reference(fb, msg->dn);
+
+       /* We add objectClass, so 'top' should be no problem */
+       ldb_msg_add_string(module->ldb, mp, "objectClass", "top");
+       
+       /* make a list of remote objectclasses that can be used 
+        *   given the attributes that are available and add to 
+        *   mp_msg */
+       for (i = 0; privdat->objectclass_maps[i].local_name; i++) {
+               int j, has_musts, has_baseclasses;
+               
+               /* Add this objectClass to the list if all musts are present */
+               for (j = 0; privdat->objectclass_maps[i].musts[j]; j++) {
+                       if (!map_msg_can_map_attr(module, msg, privdat->objectclass_maps[i].musts[j])) 
+                               break;
                }
-       }
 
-       talloc_free(fb);
+               has_musts = (privdat->objectclass_maps[i].musts[j] == NULL);
+
+               /* Check if base classes are present as well */
+               for (j = 0; privdat->objectclass_maps[i].base_classes[j]; j++) {
+                       if (!msg_contains_objectclass(mp, privdat->objectclass_maps[i].base_classes[j]))
+                               break;
+               }
+
+               has_baseclasses = (privdat->objectclass_maps[i].base_classes[j] == NULL);
                
-       if (mp != NULL) {
-               ret = ldb_add(privdat->mapped_ldb, mp);
-               if (ret == -1) {
-                       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Adding mapped record failed");
-                       return -1;
+               /* Apparently, it contains all required elements */
+               if (has_musts && has_baseclasses) {
+                       ldb_msg_add_string(module->ldb, mp, "objectClass", privdat->objectclass_maps[i].remote_name);   
                }
        }
 
+       ocs = ldb_msg_find_element(mp, "objectClass");
+       if (ocs->num_values == 1) { /* Only top */
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_map_add: Added fallback record");
+               return ldb_next_add_record(module, msg);
+       }
+       
+       /*
+        * - try to map as much attributes as possible where allowed and add them to mp_msg
+        * - add other attributes to fb_msg
+        */
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_map_attribute *attr;
+               struct ldb_message_element *elm;
+               enum ldb_map_attr_type map_type;
+               int j;
+               int mapped = 0;
+
+               if (ldb_attr_cmp(msg->elements[i].name, "objectClass") == 0)
+                       continue;
+
+               attr = map_find_attr_local(privdat, msg->elements[i].name);
+
+               if (!attr) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Undefined local attribute '%s', ignoring\n", msg->elements[i].name);
+                       map_type = MAP_IGNORE;
+               } else map_type = attr->type;
+
+               /* Decide whether or not we need to map or fallback */
+               switch (map_type) {
+               case MAP_GENERATE:
+                       attr->u.generate.generate_remote(module, attr->local_name, msg, mp, fb);
+                       continue;
+               case MAP_KEEP:
+                       mapped = map_msg_valid_attr(module, mp, attr->local_name);
+               case MAP_IGNORE: mapped = 0; break;
+               case MAP_CONVERT:
+               case MAP_RENAME: mapped = map_msg_valid_attr(module, mp, attr->u.rename.remote_name);
+                       break;
+               }
+
+               if (mapped) {
+                       switch (map_type) {
+                       case MAP_KEEP:
+                               elm = talloc(fb, struct ldb_message_element);
+
+                               elm->num_values = msg->elements[i].num_values;
+                               elm->values = talloc_reference(elm, msg->elements[i].values);
+                               elm->name = talloc_strdup(elm, msg->elements[i].name);
+                               break;
+
+                       case MAP_RENAME:
+                               elm = talloc(mp, struct ldb_message_element);
+
+                               elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
+                               elm->num_values = msg->elements[i].num_values;
+                               elm->values = talloc_reference(elm, msg->elements[i].values);
+                               break;
+
+                       case MAP_CONVERT:
+                               elm = talloc(mp, struct ldb_message_element);
+
+                               elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
+                               elm->num_values = msg->elements[i].num_values;
+                               elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
+
+                               for (j = 0; j < elm->num_values; j++) {
+                                       elm->values[j] = attr->u.convert.convert_local(module, mp, &msg->elements[i].values[j]);
+                               }
+
+                               mapped = map_msg_valid_attr(module, mp, attr->u.convert.remote_name);
+
+                               break;
+
+                       case MAP_GENERATE:
+                       case MAP_IGNORE:
+                               ldb_debug(module->ldb, LDB_DEBUG_FATAL, "This line should never be reached");
+                               break;
+                       } 
+                       ldb_msg_add(module->ldb, mp, elm, 0);
+               } else {
+                       elm = talloc(fb, struct ldb_message_element);
+
+                       elm->num_values = msg->elements[i].num_values;
+                       elm->values = talloc_reference(elm, msg->elements[i].values);
+                       elm->name = talloc_strdup(elm, msg->elements[i].name);
+
+                       ldb_msg_add(module->ldb, fb, elm, 0);
+               }
+       }
+
+       ret = ldb_add(privdat->mapped_ldb, mp);
+       if (ret == -1) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Adding mapped record failed: %s", ldb_errstring(privdat->mapped_ldb));
+               return -1;
+       }
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_map_add: Added mapped record");
+
+       ldb_msg_add_string(module->ldb, fb, "isMapped", "TRUE");
+       ret = ldb_next_add_record(module, fb);
+       if (ret == -1) {
+               ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Adding fallback record failed: %s", ldb_next_errstring(module));
+               return -1;
+       }
+
+       talloc_free(fb);
        talloc_free(mp);
 
        return ret;
@@ -1013,25 +1107,106 @@ static int map_modify(struct ldb_module *module, const struct ldb_message *msg)
 {
        struct ldb_map_context *privdat = map_get_privdat(module);
        struct ldb_message *fb, *mp;
-       int ret;
+       struct ldb_message_element *elm;
+       int fb_ret, mp_ret;
+       int i,j;
+
+       ldb_debug(module->ldb, LDB_DEBUG_TRACE, "ldb_map_modify");
 
-       if (!map_is_mappable(privdat, msg))
+       if (ldb_dn_is_special(msg->dn))
                return ldb_next_modify_record(module, msg);
 
-       if (ldb_map_message_outgoing(module, msg, &fb, &mp) == -1)
-               return -1;
+       fb = talloc_zero(module, struct ldb_message);
+       fb->dn = talloc_reference(fb, msg->dn);
+
+       mp = talloc_zero(module, struct ldb_message);
+       mp->dn = map_local_dn(module, mp, msg->dn);
+
+       /* Loop over mi and call generate_remote for each attribute */
+       for (i = 0; i < msg->num_elements; i++) {
+               const struct ldb_map_attribute *attr;
+               enum ldb_map_attr_type map_type;
+
+               if (ldb_attr_cmp(msg->elements[i].name, "isMapped") == 0)
+                       continue;
+
+               attr = map_find_attr_local(privdat, msg->elements[i].name);
+
+               if (!attr) {
+                       ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Undefined local attribute '%s', ignoring\n", msg->elements[i].name);
+                       map_type = MAP_IGNORE;
+               } else map_type = attr->type;
+       
+               switch (map_type) {
+               case MAP_IGNORE: /* Add to fallback message */
+                       elm = talloc(fb, struct ldb_message_element);
+
+                       elm->num_values = msg->elements[i].num_values;
+                       elm->values = talloc_reference(elm, msg->elements[i].values);
+                       elm->name = talloc_strdup(elm, msg->elements[i].name);
+                       
+                       ldb_msg_add(module->ldb, fb, elm, msg->elements[i].flags);      
+                       break;
+               case MAP_RENAME:
+                       elm = talloc(mp, struct ldb_message_element);
+
+                       elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
+                       elm->num_values = msg->elements[i].num_values;
+                       elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
+                       for (j = 0; j < elm->num_values; j++) {
+                               elm->values[j] = msg->elements[i].values[j];
+                       }
+
+                       ldb_msg_add(module->ldb, mp, elm, msg->elements[i].flags);
+                       break;
+
+               case MAP_CONVERT:
+                       elm = talloc(mp, struct ldb_message_element);
+
+                       elm->name = talloc_strdup(elm, attr->u.rename.remote_name);
+                       elm->num_values = msg->elements[i].num_values;
+                       elm->values = talloc_array(elm, struct ldb_val, elm->num_values);
+                       
+                       for (j = 0; j < elm->num_values; j++) {
+                               elm->values[j] = attr->u.convert.convert_local(module, mp, &msg->elements[i].values[j]);
+                       }
+
+                       ldb_msg_add(module->ldb, mp, elm, msg->elements[i].flags);
+                       break;
+
+               case MAP_KEEP:
+                       elm = talloc(mp, struct ldb_message_element);
 
-       if (fb != NULL) {
-               ret = ldb_next_modify_record(module, fb);
-               talloc_free(fb);
+                       elm->num_values = msg->elements[i].num_values;
+                       elm->values = talloc_reference(elm, msg->elements[i].values);
+                       elm->name = talloc_strdup(elm, msg->elements[i].name);
+                       
+                       ldb_msg_add(module->ldb, mp, elm, msg->elements[i].flags);      
+                       break;
+
+               case MAP_GENERATE:
+                       attr->u.generate.generate_remote(module, attr->local_name, msg, mp, fb);
+                       break;
+               } 
+       }
+
+       if (fb->num_elements > 0) {
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Modifying fallback record with %d elements", fb->num_elements);
+               fb_ret = ldb_next_modify_record(module, fb);
+               if (fb_ret == -1) {
+                       ldb_msg_add_string(module->ldb, fb, "isMapped", "TRUE");
+                       fb_ret = ldb_next_add_record(module, fb);
+               }
        }
+       talloc_free(fb);
 
-       if (mp != NULL) {
-               ret = ldb_modify(privdat->mapped_ldb, mp);
-               talloc_free(mp);
+       if (mp->num_elements > 0) {
+               ldb_debug(module->ldb, LDB_DEBUG_TRACE, "Modifying mapped record with %d elements", mp->num_elements);
+               mp_ret = ldb_modify(privdat->mapped_ldb, mp);
        }
+       talloc_free(mp);
 
-       return ret;
+       return (mp_ret == -1 || fb_ret == -1)?-1:0;
 }
 
 static int map_lock(struct ldb_module *module, const char *lockname)
index 4077660..984a4a2 100644 (file)
@@ -81,7 +81,8 @@ struct ldb_map_attribute
                                        struct ldb_module *, 
                                        const char *local_attr,
                                        const struct ldb_message *local, 
-                                       struct ldb_message *remote);
+                                       struct ldb_message *remote_mp,
+                                       struct ldb_message *remote_fb);
 
                        /* Name(s) for this attribute on the remote server. This is an array since 
                         * one local attribute's data can be split up into several attributes 
@@ -92,15 +93,22 @@ struct ldb_map_attribute
        } u;
 };
 
+#define LDB_MAP_MAX_SUBCLASSES         10
+#define LDB_MAP_MAX_MUSTS              10
+#define LDB_MAP_MAX_MAYS               50
 struct ldb_map_objectclass 
 {
        const char *local_name;
        const char *remote_name;
+       const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
+       const char *musts[LDB_MAP_MAX_MUSTS];
+       const char *mays[LDB_MAP_MAX_MAYS];
 };
 
 struct ldb_map_context
 {
        struct ldb_map_attribute *attribute_maps;
+       /* NOTE: Always declare base classes first here */
        const struct ldb_map_objectclass *objectclass_maps;
        struct ldb_context *mapped_ldb;
 };
index 45e6884..07fa8f3 100644 (file)
@@ -541,11 +541,6 @@ function upgrade(subobj, samba3, message, paths)
        // Enable samba3sam module if original passdb backend was ldap
        if (ldapurl != undefined) {
                message("Enabling Samba3 LDAP mappings for SAM database\n");
-               var ldif = sprintf("
-dn: @MAP=samba3sam
-@MAP_URL: %s", ldapurl);
-               ok = samdb.add(ldif);
-               assert(ok);
 
                ok = samdb.modify("
 dn: @MODULES
@@ -557,6 +552,12 @@ replace: @LIST
                        message("Error enabling samba3sam module: " + samdb.errstring() + "\n");
                        ret = ret + 1;
                }
+
+               ok = samdb.add(sprintf("
+dn: @MAP=samba3sam
+@MAP_URL: %s", ldapurl));
+               assert(ok);
+
        }
 
        return ret;
index 035eb1c..67c0ae1 100644 (file)
@@ -1,12 +1,19 @@
-dn: ou=Users,ou=Tests,dc=vernstok,dc=nl
+dn: sambaDomainName=TESTS,dc=vernstok,dc=nl
+objectclass: sambaDomain
+objectclass: top
+sambaSID: S-1-5-21-4231626423-2410014848-2360679739
+sambaNextRid: 2000
+sambaDomainName: TESTS
+
+dn: ou=Users,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: organizationalUnit
 ou: Users
 
-dn: ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: organizationalUnit
 ou: Groups
 
-dn: uid=nobody,ou=Users,ou=Tests,dc=vernstok,dc=nl
+dn: uid=nobody,ou=Users,sambaDomainName=TESTS,dc=vernstok,dc=nl
 cn: nobody
 sn: nobody
 objectClass: inetOrgPerson
@@ -33,7 +40,7 @@ sambaAcctFlags: [NU         ]
 sambaSID: S-1-5-21-4231626423-2410014848-2360679739-2998
 loginShell: /bin/false
 
-dn: cn=Domain Admins,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Domain Admins,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 512
@@ -44,7 +51,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-512
 sambaGroupType: 2
 displayName: Domain Admins
 
-dn: cn=Domain Users,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Domain Users,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 513
@@ -54,7 +61,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-513
 sambaGroupType: 2
 displayName: Domain Users
 
-dn: cn=Domain Guests,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Domain Guests,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 514
@@ -64,7 +71,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-514
 sambaGroupType: 2
 displayName: Domain Guests
 
-dn: cn=Print Operators,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Print Operators,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 550
@@ -74,7 +81,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-550
 sambaGroupType: 2
 displayName: Print Operators
 
-dn: cn=Backup Operators,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Backup Operators,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 551
@@ -84,7 +91,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-551
 sambaGroupType: 2
 displayName: Backup Operators
 
-dn: cn=Replicator,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Replicator,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 552
@@ -94,7 +101,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-552
 sambaGroupType: 2
 displayName: Replicator
 
-dn: cn=Domain Computers,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Domain Computers,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 553
@@ -104,7 +111,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-553
 sambaGroupType: 2
 displayName: Domain Computers
 
-dn: cn=Administrators,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Administrators,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 544
@@ -114,7 +121,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-544
 sambaGroupType: 2
 displayName: Administrators
 
-dn: cn=Users,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Users,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 545
@@ -124,7 +131,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-545
 sambaGroupType: 2
 displayName: users
 
-dn: cn=Guests,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Guests,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 546
@@ -135,7 +142,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-546
 sambaGroupType: 2
 displayName: Guests
 
-dn: cn=Power Users,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Power Users,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 547
@@ -145,7 +152,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-547
 sambaGroupType: 2
 displayName: Power Users
 
-dn: cn=Account Operators,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Account Operators,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 548
@@ -155,7 +162,7 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-548
 sambaGroupType: 2
 displayName: Account Operators
 
-dn: cn=Server Operators,ou=Groups,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Server Operators,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: posixGroup
 objectClass: sambaGroupMapping
 gidNumber: 549
@@ -165,11 +172,11 @@ sambaSID: S-1-5-21-4231626423-2410014848-2360679739-549
 sambaGroupType: 2
 displayName: Server Operators
 
-dn: ou=Computers,ou=Tests,dc=vernstok,dc=nl
+dn: ou=Computers,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: organizationalUnit
 ou: Computers
 
-dn: uid=Administrator,ou=Users,ou=Tests,dc=vernstok,dc=nl
+dn: uid=Administrator,ou=Users,sambaDomainName=TESTS,dc=vernstok,dc=nl
 cn: Administrator
 sn: Administrator
 objectClass: inetOrgPerson
index 14ddd01..c422bce 100755 (executable)
@@ -40,6 +40,16 @@ assert(ldif != undefined);
 ok = s4.add(ldif);
 assert(ok);
 
+var ldif = sys.file_load(mypath + "../../source/setup/provision_templates.ldif");
+var subobj = new Object();
+subobj.BASEDN = "dc=vernstok,dc=nl";
+ldif = substitute_var(ldif, subobj);
+assert(ldif != undefined);
+ok = s4.add(ldif);
+assert(ok);
+
+
+
 ok = s4.add(sprintf("dn: @MAP=samba3sam
 @MAP_URL: %s", s3url));
 assert(ok);
@@ -57,7 +67,6 @@ assert(ok);
 
 msg = s4.search("(ou=Users)");
 assert(msg.length == 1);
-assert(msg['mappedFromDn'] == msg['dn']);
 
 println("Looking up by non-mapped attribute");
 msg = s4.search("(cn=Administrator)");
@@ -76,9 +85,8 @@ assert(msg.length == 0);
 println("Looking up mapped entry containing SID");
 msg = s4.search("(cn=Replicator)");
 assert(msg.length == 1);
-assert(msg[0].dn == "cn=Replicator,ou=Groups,ou=Tests,dc=vernstok,dc=nl");
+assert(msg[0].dn == "cn=Replicator,ou=Groups,sambaDomainName=TESTS,dc=vernstok,dc=nl");
 assert(msg[0].objectSid == "S-1-5-21-4231626423-2410014848-2360679739-552");
-assert(msg[0].mappedFromDn == msg[0].dn);
 
 println("Checking mapping of objectclass");
 var oc = msg[0].objectclass;
@@ -89,24 +97,23 @@ for (var i in oc) {
 println("Adding a record that will be fallbacked");
 ok = s4.add("
 dn: cn=Foo,dc=idealx,dc=org
-unixName: root
-lastLogon: 20000
+foo: bar
+blah: Blie
 cn: Foo
 showInAdvancedViewOnly: TRUE
 ");
 assert(ok);
 
 println("Checking for existance of record");
-msg = s4.search("(cn=Foo)", new Array('unixName','lastLogon','cn','showInAdvancedViewOnly'));
+msg = s4.search("(cn=Foo)", new Array('foo','blah','cn','showInAdvancedViewOnly'));
 assert(msg.length == 1);
 assert(msg[0].showInAdvancedViewOnly == "TRUE");
-assert(msg[0].cn == "Foo");
-assert(msg[0].unixName == "root");
-assert(msg[0].lastLogon == 20000);
+assert(msg[0].foo == "bar");
+assert(msg[0].blah == "Blie");
 
 println("Adding record that will be mapped");
 ok = s4.add("
-dn: cn=Niemand,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
 objectClass: user
 unixName: blah
 cn: Niemand
@@ -116,15 +123,17 @@ assert(ok);
 println("Checking for existance of record (mapped)");
 msg = s4.search("(unixName=blah)", new Array('unixName','cn','dn'));
 assert(msg.length == 1);
+assert(msg[0].cn == "Niemand");
 
 println("Checking for data in destination database");
-msg = s3.search("(cn=Niemand)");
+msg = s3.search("(displayName=Niemand)");
 assert(msg.length >= 1);
+assert(msg[0].sambaSID == "S-1-5-21-4231626423-2410014848-2360679739-2001");
 assert(msg[0].displayName == "Niemand");
 
 println("Adding attribute...");
 ok = s4.modify("
-dn: cn=Niemand,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
 changetype: modify
 add: description
 description: Blah
@@ -134,11 +143,13 @@ assert(ok);
 println("Checking whether changes are still there...");
 msg = s4.search("(cn=Niemand)");
 assert(msg.length >= 1);
+printVars(msg);
+assert(msg[0].cn == "Niemand");
 assert(msg[0].description == "Blah");
 
 println("Modifying attribute...");
 ok = s4.modify("
-dn: cn=Niemand,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
 changetype: modify
 replace: description
 description: Blie
@@ -152,7 +163,7 @@ assert(msg[0].description == "Blie");
 
 println("Deleting attribute...");
 ok = s4.modify("
-dn: cn=Niemand,ou=Tests,dc=vernstok,dc=nl
+dn: cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl
 changetype: modify
 delete: description
 ");
@@ -164,7 +175,7 @@ assert(msg.length >= 1);
 assert(msg[0].description == undefined);
 
 println("Renaming record...");
-ok = s4.rename("cn=Niemand,ou=Tests,dc=vernstok,dc=nl", "cn=Niemand,dc=vernstok,dc=nl");
+ok = s4.rename("cn=Niemand,sambaDomainName=TESTS,dc=vernstok,dc=nl", "cn=Niemand,dc=vernstok,dc=nl");
 
 println("Checking whether DN has changed...");
 msg = s4.search("(cn=Niemand)");