Browse Source

Bug 635009 - Add one-way AD sync capability

This patch adds one-way AD sync capability. A new "oneWaySync"
configuration attribute can be added to a sync agreement to specify
which direction one-way sync should work (values of "toWindows" and
"fromWindows" will be accepted).

Internally, the server will just skip pushing/pulling updates depending
on the direction that sync is configured for.  We will still update the
RUV as if entries were actually synced.

One can introduce inconsistencies by updating synced entries on the
receiving side when using one way sync.  This should be avoided by
restricting update access to synced entries.
Nathan Kinder 15 years ago
parent
commit
8db7770486

+ 2 - 1
ldap/schema/02common.ldif

@@ -139,6 +139,7 @@ attributeTypes: ( 2.16.840.1.113730.3.1.1003 NAME 'nsds7NewWinGroupSyncEnabled'
 attributeTypes: ( 2.16.840.1.113730.3.1.1004 NAME 'nsds7WindowsDomain' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.1005 NAME 'nsds7DirsyncCookie' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 2.16.840.1.113730.3.1.1099 NAME 'winSyncInterval' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
+attributeTypes: ( 2.16.840.1.113730.3.1.1100 NAME 'oneWaySync' DESC 'Netscape defined attribute type'  SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'Netscape Directory Server' )
 attributeTypes: ( 1.3.6.1.1.4 NAME 'vendorName' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 1.3.6.1.1.5 NAME 'vendorVersion' EQUALITY 1.3.6.1.4.1.1466.109.114.1 SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE NO-USER-MODIFICATION USAGE dSAOperation X-ORIGIN 'RFC 3045' )
 attributeTypes: ( 2.16.840.1.113730.3.1.3023 NAME 'nsViewFilter' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 X-ORIGIN 'Netscape Directory Server' )
@@ -173,7 +174,7 @@ objectClasses: ( 2.16.840.1.113730.3.2.99 NAME 'cosSuperDefinition' DESC 'Netsca
 objectClasses: ( 2.16.840.1.113730.3.2.100 NAME 'cosClassicDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn $ cosspecifier ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.101 NAME 'cosPointerDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosTemplateDn ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.102 NAME 'cosIndirectDefinition' DESC 'Netscape defined objectclass' SUP cosSuperDefinition MAY ( cosIndirectSpecifier ) X-ORIGIN 'Netscape Directory Server' )
-objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval) X-ORIGIN 'Netscape Directory Server' )
+objectClasses: ( 2.16.840.1.113730.3.2.503 NAME 'nsDSWindowsReplicationAgreement' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsDS5ReplicaHost $ nsDS5ReplicaPort $ nsDS5ReplicaTransportInfo $ nsDS5ReplicaBindDN $ nsDS5ReplicaCredentials $ nsDS5ReplicaBindMethod $ nsDS5ReplicaRoot $ nsDS5ReplicatedAttributeList $ nsDS5ReplicaUpdateSchedule $ nsds5BeginReplicaRefresh $ description $ nsds50ruv $ nsruvReplicaLastModified $ nsds5ReplicaTimeout $ nsds5replicaChangesSentSinceStartup $ nsds5replicaLastUpdateEnd $ nsds5replicaLastUpdateStart $ nsds5replicaLastUpdateStatus $ nsds5replicaUpdateInProgress $ nsds5replicaLastInitEnd $ nsds5replicaLastInitStart $ nsds5replicaLastInitStatus $ nsds5debugreplicatimeout $ nsds5replicaBusyWaitTime $ nsds5replicaSessionPauseTime $ nsds7WindowsReplicaSubtree $ nsds7DirectoryReplicaSubtree $ nsds7NewWinUserSyncEnabled $ nsds7NewWinGroupSyncEnabled $ nsds7WindowsDomain $ nsds7DirsyncCookie $ winSyncInterval $ oneWaySync) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.128 NAME 'costemplate' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ cospriority ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.304 NAME 'nsView' DESC 'Netscape defined objectclass' SUP top AUXILIARY MAY ( nsViewFilter $ description ) X-ORIGIN 'Netscape Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.316 NAME 'nsAttributeEncryption' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsEncryptionAlgorithm ) X-ORIGIN 'Netscape Directory Server' )

+ 2 - 0
ldap/servers/plugins/replication/repl5.h

@@ -156,6 +156,8 @@ extern const char *type_nsds7CreateNewGroups;
 extern const char *type_nsds7DirsyncCookie;
 extern const char *type_nsds7WindowsDomain;
 extern const char *type_winSyncInterval;
+extern const char *type_oneWaySync;
+
 /* To Allow Consumer Initialisation when adding an agreement - */
 extern const char *type_nsds5BeginReplicaRefresh;
 

+ 1 - 0
ldap/servers/plugins/replication/repl_globals.c

@@ -134,6 +134,7 @@ const char *type_nsds7CreateNewGroups = "nsds7NewWinGroupSyncEnabled";
 const char *type_nsds7WindowsDomain = "nsds7WindowsDomain";
 const char *type_nsds7DirsyncCookie = "nsds7DirsyncCookie";
 const char *type_winSyncInterval = "winSyncInterval";
+const char *type_oneWaySync = "oneWaySync";
 
 /* To Allow Consumer Initialisation when adding an agreement - */
 const char *type_nsds5BeginReplicaRefresh = "nsds5BeginReplicaRefresh";

+ 20 - 5
ldap/servers/plugins/replication/windows_inc_protocol.c

@@ -139,7 +139,7 @@ typedef struct windows_inc_private
 static PRUint32 event_occurred(Private_Repl_Protocol *prp, PRUint32 event);
 static void reset_events (Private_Repl_Protocol *prp);
 static void protocol_sleep(Private_Repl_Protocol *prp, PRIntervalTime duration);
-static int send_updates(Private_Repl_Protocol *prp, RUV *ruv, PRUint32 *num_changes_sent);
+static int send_updates(Private_Repl_Protocol *prp, RUV *ruv, PRUint32 *num_changes_sent, int do_send);
 static void windows_inc_backoff_expired(time_t timer_fire_time, void *arg);
 static int windows_examine_update_vector(Private_Repl_Protocol *prp, RUV *ruv);
 static const char* state2name (int state);
@@ -294,6 +294,7 @@ windows_inc_run(Private_Repl_Protocol *prp)
 	long busywaittime = 0;
 	/* Some operations should only be done the first time STATE_START is true. */
 	static PRBool is_first_start = PR_TRUE;
+	int one_way;
  
 	PRBool run_dirsync = PR_FALSE;
 
@@ -303,6 +304,8 @@ windows_inc_run(Private_Repl_Protocol *prp)
 	prp->terminate = 0;
 
 	windows_private_load_dirsync_cookie(prp->agmt);
+
+	one_way = windows_private_get_one_way(prp->agmt);
 	
 	do {
 		int rc = 0;
@@ -829,7 +832,14 @@ windows_inc_run(Private_Repl_Protocol *prp)
 						    windows_private_get_windows_subtree(prp->agmt),
 						    0 /* is_total == FALSE */);
 
-		rc = send_updates(prp, ruv, &num_changes_sent);
+		if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) {
+		    rc = send_updates(prp, ruv, &num_changes_sent, 1 /* actually send updates */);
+		} else {
+		    /* We call send_updates to fast-forward the RUV
+		     * without actually sending any updates. */
+		    rc = send_updates(prp, ruv, &num_changes_sent, 0 /* don't send updates */);
+		}
+
 		if (rc == UPDATE_NO_MORE_UPDATES)
 		  {
 		    dev_debug("windows_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_NO_MORE_UPDATES -> STATE_WAIT_CHANGES");
@@ -877,7 +887,7 @@ windows_inc_run(Private_Repl_Protocol *prp)
 	    break;
 	  }
 
-	if ( run_dirsync )
+	if ( run_dirsync && ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD)))
 	{
 		/* Don't run dirsync if we encountered
 		 * an error when sending updates to AD. */
@@ -1151,7 +1161,7 @@ w_cl5_operation_parameters_done (struct slapi_operation_parameters *sop)
  * UPDATE_SCHEDULE_WINDOW_CLOSED - the schedule window closed on us.
  */
 static int
-send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *num_changes_sent)
+send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *num_changes_sent, int do_send)
 {
 	CL5Entry entry;
 	slapi_operation_parameters op;
@@ -1305,7 +1315,12 @@ send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *nu
 				    continue;
                 }
 				/* This is where the work actually happens: */
-				replay_crc = windows_replay_update(prp, entry.op);
+				if (do_send) {
+					replay_crc = windows_replay_update(prp, entry.op);
+				} else {
+					/* Skip sending the updates and just set a success code. */
+					replay_crc = CONN_OPERATION_SUCCESS;
+				}
 				if (CONN_OPERATION_SUCCESS != replay_crc)
 				{
 					int operation, error;

+ 57 - 0
ldap/servers/plugins/replication/windows_private.c

@@ -75,6 +75,7 @@ struct windowsprivate {
   int keep_raw_entry; /* flag to control when the raw entry is set */
   void *api_cookie; /* private data used by api callbacks */
   time_t sync_interval; /* how often to run the dirsync search, in seconds */
+  int one_way; /* Indicates if this is a one-way agreement and which direction it is */
 };
 
 static void windows_private_set_windows_domain(const Repl_Agmt *ra, char *domain);
@@ -165,6 +166,30 @@ windows_parse_config_entry(Repl_Agmt *ra, const char *type, Slapi_Entry *e)
 		slapi_ch_free_string(&tmpstr);
 		retval = 1;
 	}
+	if (type == NULL || slapi_attr_types_equivalent(type,type_oneWaySync))
+	{
+		tmpstr = slapi_entry_attr_get_charptr(e, type_oneWaySync);
+		if (NULL != tmpstr)
+		{
+			if (strcasecmp(tmpstr, "fromWindows") == 0) {
+				windows_private_set_one_way(ra, ONE_WAY_SYNC_FROM_AD);
+			} else if (strcasecmp(tmpstr, "toWindows") == 0) {
+				windows_private_set_one_way(ra, ONE_WAY_SYNC_TO_AD);
+			} else {
+				slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+					"Ignoring illegal setting for %s attribute in replication "
+					"agreement \"%s\".  Valid values are \"toWindows\" or "
+					"\"fromWindows\".\n", type_oneWaySync, slapi_entry_get_dn(e));
+				windows_private_set_one_way(ra, ONE_WAY_SYNC_DISABLED);
+			}
+		}
+		else
+		{
+			windows_private_set_one_way(ra, ONE_WAY_SYNC_DISABLED);
+		}
+		slapi_ch_free((void**)&tmpstr);
+		retval = 1;
+	}
 	return retval;
 }
 
@@ -533,6 +558,38 @@ void windows_private_set_create_groups(const Repl_Agmt *ra, PRBool value)
 }
 
 
+int windows_private_get_one_way(const Repl_Agmt *ra)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_get_one_way\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_get_one_way\n" );
+
+	return dp->one_way;
+}
+
+
+void windows_private_set_one_way(const Repl_Agmt *ra, int value)
+{
+	Dirsync_Private *dp;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_private_set_one_way\n" );
+
+	PR_ASSERT(ra);
+	dp = (Dirsync_Private *) agmt_get_priv(ra);
+	PR_ASSERT (dp);
+
+	dp->one_way = value;
+
+	LDAPDebug0Args( LDAP_DEBUG_TRACE, "<= windows_private_set_one_way\n" );
+}
+
+
 /* 
 	This function returns the current Dirsync_Private that's inside 
 	Repl_Agmt ra as a ldap control.

+ 47 - 37
ldap/servers/plugins/replication/windows_tot_protocol.c

@@ -96,9 +96,9 @@ static void windows_tot_delete(Private_Repl_Protocol **prp);
 static void
 windows_tot_run(Private_Repl_Protocol *prp)
 {
-    int rc;
-    callback_data cb_data;
-    Slapi_PBlock *pb;
+	int rc;
+	callback_data cb_data;
+	Slapi_PBlock *pb;
 	char* dn;
 	RUV *ruv = NULL;
 	RUV *starting_ruv = NULL;
@@ -108,6 +108,7 @@ windows_tot_run(Private_Repl_Protocol *prp)
 	char *filter = slapi_ch_strdup("(|(objectclass=ntuser)(objectclass=ntgroup))");
 	char **attrs = NULL;
 	LDAPControl **server_controls = NULL;
+	int one_way;
 	
 	LDAPDebug0Args( LDAP_DEBUG_TRACE, "=> windows_tot_run\n" );
 	
@@ -120,35 +121,37 @@ windows_tot_run(Private_Repl_Protocol *prp)
 		goto done;
 	}
 
+	one_way = windows_private_get_one_way(prp->agmt);
+
 	windows_conn_set_timeout(prp->conn, agmt_get_timeout(prp->agmt));
 
-    /* acquire remote replica */
+	/* acquire remote replica */
 	agmt_set_last_init_start(prp->agmt, current_time());
 	
-    rc = windows_acquire_replica (prp, &ruv, 0 /* don't check RUV for total protocol */);
-    /* We never retry total protocol, even in case a transient error.
-       This is because if somebody already updated the replica we don't
-       want to do it again */
-    if (rc != ACQUIRE_SUCCESS)
-    {
+	rc = windows_acquire_replica (prp, &ruv, 0 /* don't check RUV for total protocol */);
+	/* We never retry total protocol, even in case a transient error.
+	 * This is because if somebody already updated the replica we don't
+	 * want to do it again */
+	if (rc != ACQUIRE_SUCCESS)
+	{
 		int optype, ldaprc;
 		windows_conn_get_error(prp->conn, &optype, &ldaprc);
 		agmt_set_last_init_status(prp->agmt, ldaprc,
 				  prp->last_acquire_response_code, NULL);
-        goto done;
-    }
+		goto done;
+	}
 	else if (prp->terminate)
-    {
-        windows_conn_disconnect(prp->conn);
-        prp->stopped = 1;
+	{
+		windows_conn_disconnect(prp->conn);
+		prp->stopped = 1;
 		goto done;    
-    }
+	}
 
 	agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update in progress");
 
-    agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress");
+	agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress");
 
-    slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Beginning total update of replica "
+	slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "Beginning total update of replica "
 		"\"%s\".\n", agmt_get_long_name(prp->agmt));
     
 	windows_private_null_dirsync_cookie(prp->agmt);
@@ -159,8 +162,10 @@ windows_tot_run(Private_Repl_Protocol *prp)
 										windows_private_get_windows_subtree(prp->agmt),
 										1 /* is_total == TRUE */);
 
-	/* get everything */
-	windows_dirsync_inc_run(prp);
+	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_FROM_AD)) {
+		/* get everything */
+		windows_dirsync_inc_run(prp);
+	}
 	
 	windows_private_save_dirsync_cookie(prp->agmt);
 
@@ -176,28 +181,33 @@ windows_tot_run(Private_Repl_Protocol *prp)
         starting_ruv = ruv_dup((RUV*)  object_get_data ( local_ruv_obj ));
         object_release (local_ruv_obj);
 	
+	/* Set up the callback data. */
+	cb_data.prp = prp;
+	cb_data.rc = 0;
+	cb_data.num_entries = 0UL;
+	cb_data.sleep_on_busy = 0UL;
+	cb_data.last_busy = current_time ();
 
-	/* send everything */
-	dn = slapi_ch_strdup(slapi_sdn_get_dn( windows_private_get_directory_subtree(prp->agmt)));
+	/* Don't send anything if one-way is set. */
+	if ((one_way == ONE_WAY_SYNC_DISABLED) || (one_way == ONE_WAY_SYNC_TO_AD)) {
+		/* send everything */
+		dn = slapi_ch_strdup(slapi_sdn_get_dn( windows_private_get_directory_subtree(prp->agmt)));
 
-	winsync_plugin_call_pre_ds_search_all_cb(prp->agmt, NULL, &dn, &scope, &filter,
+		winsync_plugin_call_pre_ds_search_all_cb(prp->agmt, NULL, &dn, &scope, &filter,
 											 &attrs, &server_controls);
 
-	pb = slapi_pblock_new ();
-    /* Perform a subtree search for any ntuser or ntgroup entries underneath the
-     * suffix defined in the sync agreement. */
-    slapi_search_internal_set_pb (pb, dn, scope, filter, attrs, 0, server_controls, NULL, 
-                                  repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
-    cb_data.prp = prp;
-    cb_data.rc = 0;
-	cb_data.num_entries = 0UL;
-	cb_data.sleep_on_busy = 0UL;
-	cb_data.last_busy = current_time ();
+		pb = slapi_pblock_new ();
+		/* Perform a subtree search for any ntuser or ntgroup entries underneath the
+		 * suffix defined in the sync agreement. */
+		slapi_search_internal_set_pb (pb, dn, scope, filter, attrs, 0, server_controls, NULL, 
+				repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+		slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
+				get_result /* result callback */,
+				send_entry /* entry callback */,
+				NULL /* referral callback*/);
+	}
 
-    slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
-                                       get_result /* result callback */,
-                                       send_entry /* entry callback */,
-	    					           NULL /* referral callback*/);
     slapi_ch_free_string(&dn);
     slapi_ch_free_string(&filter);
     slapi_ch_array_free(attrs);

+ 9 - 0
ldap/servers/plugins/replication/windowsrepl.h

@@ -84,6 +84,8 @@ void *windows_private_get_api_cookie(const Repl_Agmt *ra);
 void windows_private_set_api_cookie(Repl_Agmt *ra, void *cookie);
 time_t windows_private_get_sync_interval(const Repl_Agmt *ra);
 void windows_private_set_sync_interval(Repl_Agmt *ra, char *str);
+PRBool windows_private_get_one_way(const Repl_Agmt *ra);
+void windows_private_set_one_way(const Repl_Agmt *ra, PRBool value);
 
 /* in windows_connection.c */
 ConnResult windows_conn_connect(Repl_Connection *conn);
@@ -133,6 +135,13 @@ int windows_check_user_password(Repl_Connection *conn, Slapi_DN *sdn, char *pass
  */
 #define PERIODIC_DIRSYNC_INTERVAL 5 * 60 /* default value is 5 minutes */
 
+/*
+ * One way sync flags.  Used to indicate the direction when one-way sync is used.
+ */
+#define ONE_WAY_SYNC_DISABLED 0
+#define ONE_WAY_SYNC_FROM_AD 1
+#define ONE_WAY_SYNC_TO_AD 2
+
 /* called for each replication agreement - so the winsync
    plugin can be agreement specific and store agreement
    specific data