Пример #1
0
        protected override DocumentStore GetDocumentStore(Options options = null, [CallerMemberName] string caller = null)
        {
            // since we want server to survive between tests runs
            // we have to cheat a little bit
            // benchmark tests are divided into 2 phases:
            // 1. initialization
            // 2. actual test execution (this part is measured)

            var server = Server;

            if (Servers.Contains(server) == false)
            {
                Servers.Add(server);
            }

            if (options == null)
            {
                options = new Options();
            }

            options.ModifyDatabaseRecord = record => record.Settings.Remove(RavenConfiguration.GetKey(x => x.Core.RunInMemory));

            if (Encrypted)
            {
                options.ClientCertificate = Certificates.GenerateAndSaveSelfSignedCertificate().ServerCertificate.Value;
            }

            return(base.GetDocumentStore(options, caller));
        }
Пример #2
0
        private RavenServer CreateSecuredServer(string fakePublicUrl = null, bool uniqueCerts = false)
        {
            var certificates   = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: uniqueCerts);
            var customSettings = new ConcurrentDictionary <string, string>();

            if (customSettings.TryGetValue(RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec), out var _) == false)
            {
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificatePath)] = certificates.ServerCertificatePath;
            }

            customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://localhost:0";

            RavenServer ravenServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = customSettings
            });

            if (fakePublicUrl != null)
            {
                ravenServer.Configuration.Core.ExternalPublicTcpServerUrl =
                    new[] { fakePublicUrl }.Concat(ravenServer.ServerStore.GetNodeClusterTcpServerUrls(forExternalUse: true))
                .Select(x => new UriSetting(x)).ToArray();
                ravenServer.Configuration.Core.ClusterPublicTcpServerUrl = new[] { fakePublicUrl }.Concat(ravenServer.ServerStore.GetNodeClusterTcpServerUrls(forExternalUse: false))
                .Select(x => new UriSetting(x)).ToArray();
                ravenServer.Configuration.Core.PublicTcpServerUrl = new UriSetting(fakePublicUrl);
            }

            return(ravenServer);
        }
Пример #3
0
        public async Task Server_store_basic_read_write_should_work()
        {
            using (GetDocumentStore())
            {
                var certificate = new X509Certificate2(Certificates.GenerateAndSaveSelfSignedCertificate().ServerCertificatePath, (string)null, X509KeyStorageFlags.MachineKeySet);

                TransactionOperationContext context;
                using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                {
                    await Server.ServerStore.PutValueInClusterAsync(new PutCertificateCommand(certificate.Thumbprint, new CertificateDefinition
                    {
                        Name = "foo/bar",
                        Certificate = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)),
                        Permissions = new Dictionary <string, DatabaseAccess>(),
                        SecurityClearance = SecurityClearance.ClusterAdmin,
                        Thumbprint = certificate.Thumbprint,
                        PublicKeyPinningHash = certificate.GetPublicKeyPinningHash(),
                        NotAfter = certificate.NotAfter
                    }, string.Empty));
                }

                using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                    using (context.OpenReadTransaction())
                    {
                        var    fetched = Server.ServerStore.Cluster.GetCertificateByThumbprint(context, certificate.Thumbprint);
                        string val;
                        Assert.True(fetched.TryGet(nameof(CertificateDefinition.Thumbprint), out val));
                        Assert.Equal(certificate.Thumbprint, val);
                    }
            }
        }
Пример #4
0
        public async Task PullReplicationWithoutPrivateKey()
        {
            var hubSettings  = new ConcurrentDictionary <string, string>();
            var sinkSettings = new ConcurrentDictionary <string, string>();

            var hubCertificates = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: true);
            var hubCerts        = Certificates.SetupServerAuthentication(hubSettings, certificates: hubCertificates);

            var sinkCertificates = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: true);
            var sinkCerts        = Certificates.SetupServerAuthentication(sinkSettings, certificates: sinkCertificates);

            var hubDB  = GetDatabaseName();
            var sinkDB = GetDatabaseName();
            var pullReplicationName = $"{hubDB}-pull";

            var hubServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = hubSettings, RegisterForDisposal = true
            });
            var sinkServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = sinkSettings, RegisterForDisposal = true
            });

            var dummy = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: true);
            var pullReplicationCertificate = new X509Certificate2(dummy.ServerCertificatePath, (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

            Assert.True(pullReplicationCertificate.HasPrivateKey);

            using (var hubStore = GetDocumentStore(new Options
            {
                ClientCertificate = hubCerts.ServerCertificate.Value,
                Server = hubServer,
                ModifyDatabaseName = _ => hubDB
            }))
                using (var sinkStore = GetDocumentStore(new Options
                {
                    ClientCertificate = sinkCerts.ServerCertificate.Value,
                    Server = sinkServer,
                    ModifyDatabaseName = _ => sinkDB
                }))
                {
                    var pull = new PullReplicationAsSink(hubStore.Database, $"ConnectionString-{hubStore.Database}", pullReplicationName);
                    pull.CertificateWithPrivateKey = Convert.ToBase64String(pullReplicationCertificate.Export(X509ContentType.Cert));

                    await Assert.ThrowsAsync <AuthorizationException>(async() => await sinkStore.Maintenance.SendAsync(new UpdatePullReplicationAsSinkOperation(pull)));
                }
        }
Пример #5
0
        protected DocumentStore GetSimpleDocumentStore(string databaseName, bool deleteDatabaseOnDispose = true)
        {
            X509Certificate2 adminCert = null;

            if (Encrypted)
            {
                var certificates = Certificates.GenerateAndSaveSelfSignedCertificate();
                adminCert = Certificates.RegisterClientCertificate(certificates.ServerCertificate.Value,
                                                                   certificates.ClientCertificate1.Value,
                                                                   new Dictionary <string, DatabaseAccess>(), SecurityClearance.ClusterAdmin,
                                                                   Server);
            }


            var store = new DocumentStore
            {
                Urls = new[]
                {
                    Server.WebUrl
                },
                Database    = databaseName,
                Certificate = adminCert
            };

            if (deleteDatabaseOnDispose)
            {
                store.BeforeDispose += (sender, args) =>
                {
                    try
                    {
                        store.Maintenance.Server.Send(new DeleteDatabasesOperation(databaseName, hardDelete: true));
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                };
            }

            store.Initialize();

            return(store);
        }
Пример #6
0
        public async Task Server_store_write_should_throw_concurrency_exception_if_relevant()
        {
            using (GetDocumentStore())
            {
                var certificate = new X509Certificate2(Certificates.GenerateAndSaveSelfSignedCertificate().ServerCertificatePath, (string)null, X509KeyStorageFlags.MachineKeySet);

                TransactionOperationContext context;
                using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                    using (context.OpenWriteTransaction())
                    {
                        using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                        {
                            await Server.ServerStore.PutValueInClusterAsync(new PutCertificateCommand("foo/bar", new CertificateDefinition
                            {
                                Certificate = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)),
                                Permissions = null,
                                SecurityClearance = SecurityClearance.ClusterAdmin,
                                Thumbprint = certificate.Thumbprint,
                                PublicKeyPinningHash = certificate.GetPublicKeyPinningHash(),
                                NotAfter = certificate.NotAfter
                            }, string.Empty));
                        }
                    }

                using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                {
                    using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                    {
                        await Server.ServerStore.PutValueInClusterAsync(new PutCertificateCommand("foo/bar", new CertificateDefinition
                        {
                            Certificate = Convert.ToBase64String(certificate.Export(X509ContentType.Cert)),
                            Permissions = null,
                            SecurityClearance = SecurityClearance.ClusterAdmin,
                            Thumbprint = certificate.Thumbprint,
                            PublicKeyPinningHash = certificate.GetPublicKeyPinningHash(),
                            NotAfter = certificate.NotAfter
                        }, string.Empty));
                    }
                }

                using (Server.ServerStore.ContextPool.AllocateOperationContext(out context))
                {
                    var foo = new DynamicJsonValue
                    {
                        ["Foo"] = "Bar3"
                    };

                    using (var blittableObj = context.ReadObject(foo, "read test stuff"))
                    {
                        //TODO: Restore this.

                        ////this shouldn't throw, since expected etag == null
                        //Server.ServerStore.PutValueInClusterAsync(context, "foo/bar", blittableObj).Wait();

                        //var lastEtag = Server.ServerStore.ReadLastEtag(context);
                        ////this shouldn't throw, since expected etag == existing etag
                        //Server.ServerStore.Write(context, "foo/bar", blittableObj, lastEtag);

                        ////this should throw because existing etag doesn't match with existing etag
                        //Assert.Throws<global::Voron.Exceptions.ConcurrencyException>(
                        //    () => Server.ServerStore.Write(context, "foo/bar", blittableObj, 1));

                        ////this should throw because it has expected etag, but there is no existing value
                        //Assert.Throws<global::Voron.Exceptions.ConcurrencyException>(
                        //    () => Server.ServerStore.Write(context, "foo/bar2", blittableObj, 1));
                    }
                }
            }
        }
Пример #7
0
        public async Task CertificateAndMasterKeyExecTest()
        {
            string script;
            IDictionary <string, string> customSettings = new ConcurrentDictionary <string, string>();
            var keyPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            var buffer  = new byte[256 / 8];

            using (var cryptoRandom = RandomNumberGenerator.Create())
            {
                cryptoRandom.GetBytes(buffer);
            }
            File.WriteAllBytes(keyPath, buffer);
            var certificates = Certificates.GenerateAndSaveSelfSignedCertificate();

            if (PlatformDetails.RunningOnPosix)
            {
                var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".sh"));
                var keyArgs    = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> {
                    scriptPath, keyPath
                });
                var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> {
                    scriptPath, certificates.ServerCertificatePath
                });

                customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)]                = "bash";
                customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)]       = $"{keyArgs}";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)]          = "bash";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}";
                customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0";

                script = "#!/bin/bash\ncat \"$1\"";
                File.WriteAllText(scriptPath, script);
                Process.Start("chmod", $"700 {scriptPath}");
            }
            else
            {
                var scriptPath = Path.Combine(Path.GetTempPath(), Path.ChangeExtension(Guid.NewGuid().ToString(), ".ps1"));
                var keyArgs    = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> {
                    "-NoProfile", scriptPath, keyPath
                });
                var certArgs = CommandLineArgumentEscaper.EscapeAndConcatenate(new List <string> {
                    "-NoProfile", scriptPath, certificates.ServerCertificatePath
                });

                customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExec)]                = "powershell";
                customSettings[RavenConfiguration.GetKey(x => x.Security.MasterKeyExecArguments)]       = $"{keyArgs}";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExec)]          = "powershell";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateRenewExec)]         = "powershell";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateChangeExec)]        = "powershell";
                customSettings[RavenConfiguration.GetKey(x => x.Security.CertificateLoadExecArguments)] = $"{certArgs}";
                customSettings[RavenConfiguration.GetKey(x => x.Core.ServerUrls)] = "https://" + Environment.MachineName + ":0";
                script = @"param([string]$userArg)
try {
    $bytes = Get-Content -path $userArg -encoding Byte
    $stdout = [System.Console]::OpenStandardOutput()
    $stdout.Write($bytes, 0, $bytes.Length)
}
catch {
    Write-Error $_.Exception
    exit 1
}
exit 0";
                File.WriteAllText(scriptPath, script);
            }

            UseNewLocalServer(customSettings: customSettings, runInMemory: false);
            // The master key loading is lazy, let's put a database secret key to invoke it.
            var dbName      = GetDatabaseName();
            var databaseKey = new byte[32];

            using (var rand = RandomNumberGenerator.Create())
            {
                rand.GetBytes(databaseKey);
            }
            var base64Key = Convert.ToBase64String(databaseKey);

            // sometimes when using `dotnet xunit` we get platform not supported from ProtectedData
            try
            {
#pragma warning disable CA1416 // Validate platform compatibility
                ProtectedData.Protect(Encoding.UTF8.GetBytes("Is supported?"), null, DataProtectionScope.CurrentUser);
#pragma warning restore CA1416 // Validate platform compatibility
            }
            catch (PlatformNotSupportedException)
            {
                return;
            }

            await Server.ServerStore.EnsureNotPassiveAsync();

            Server.ServerStore.PutSecretKey(base64Key, dbName, true);
            X509Certificate2 serverCertificate;
            try
            {
                serverCertificate = new X509Certificate2(certificates.ServerCertificatePath, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
            }
            catch (CryptographicException e)
            {
                throw new CryptographicException($"Failed to load the test certificate from {certificates}.", e);
            }
            using (var store = GetDocumentStore(new Options
            {
                AdminCertificate = serverCertificate,
                ClientCertificate = serverCertificate,
                ModifyDatabaseName = s => dbName,
                ModifyDatabaseRecord = record => record.Encrypted = true,
                Path = NewDataPath()
            }))
            {
            }
            var secrets         = Server.ServerStore.Secrets;
            var serverMasterKey = (Lazy <byte[]>) typeof(SecretProtection).GetField("_serverMasterKey", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(secrets);
            Assert.True(serverMasterKey.Value.SequenceEqual(buffer));
            Assert.True(Server.Certificate.Certificate.Equals(serverCertificate));
        }
Пример #8
0
        public async Task PullExternalReplicationWithCertificateShouldWork()
        {
            var hubSettings  = new ConcurrentDictionary <string, string>();
            var sinkSettings = new ConcurrentDictionary <string, string>();

            var hubCertificates = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: true);
            var hubCerts        = Certificates.SetupServerAuthentication(hubSettings, certificates: hubCertificates);

            var sinkCertificates = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: false);
            var sinkCerts        = Certificates.SetupServerAuthentication(sinkSettings, certificates: sinkCertificates);

            var hubDB  = GetDatabaseName();
            var sinkDB = GetDatabaseName();
            var pullReplicationName = $"{hubDB}-pull";

            var hubServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = hubSettings, RegisterForDisposal = true
            });
            var sinkServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = sinkSettings, RegisterForDisposal = true
            });

            var dummy = Certificates.GenerateAndSaveSelfSignedCertificate(createNew: false);
            var pullReplicationCertificate = new X509Certificate2(dummy.ServerCertificatePath, (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

            Assert.True(pullReplicationCertificate.HasPrivateKey);

            using (var hubStore = GetDocumentStore(new Options
            {
                ClientCertificate = hubCerts.ServerCertificate.Value,
                Server = hubServer,
                ModifyDatabaseName = _ => hubDB
            }))
                using (var sinkStore = GetDocumentStore(new Options
                {
                    ClientCertificate = sinkCerts.ServerCertificate.Value,
                    Server = sinkServer,
                    ModifyDatabaseName = _ => sinkDB
                }))
                {
                    await hubStore.Maintenance.SendAsync(new PutPullReplicationAsHubOperation(new PullReplicationDefinition(pullReplicationName)
                    {
#pragma warning disable CS0618 // Type or member is obsolete
                        Certificates = new Dictionary <string, string>
#pragma warning restore CS0618 // Type or member is obsolete
                        {
                            [pullReplicationCertificate.Thumbprint] = Convert.ToBase64String(pullReplicationCertificate.Export(X509ContentType.Cert))
                        }
                    }));

                    var configurationResult = await SetupPullReplicationAsync(pullReplicationName, sinkStore, pullReplicationCertificate, hubStore);

                    var sinkTaskId = configurationResult[0].TaskId;
                    using (var hubSession = hubStore.OpenSession())
                    {
                        hubSession.Store(new User(), "foo/bar");
                        hubSession.SaveChanges();
                    }

                    var timeout = 5000;
                    Assert.True(WaitForDocument(sinkStore, "foo/bar", timeout), sinkStore.Identifier);

                    // test if certificate is retained when we don't send one
                    // sending null as cert - but it should copy old one
                    await sinkStore.Maintenance.SendAsync(new UpdatePullReplicationAsSinkOperation(new PullReplicationAsSink
                    {
                        TaskId = sinkTaskId,
                        Name = pullReplicationName,
                        HubName = pullReplicationName,
                        ConnectionStringName = "ConnectionString-" + hubStore.Database
                    }));

                    using (var hubSession = hubStore.OpenSession())
                    {
                        hubSession.Store(new User(), "foo/bar2");
                        hubSession.SaveChanges();
                    }

                    var sinkDBInstance = await sinkServer.ServerStore.DatabasesLandlord.TryGetOrCreateResourceStore(sinkStore.Database);

                    Assert.True(WaitForDocument(sinkStore, "foo/bar2", timeout),
                                $"incoming handlers on sink {sinkStore.Identifier}: {sinkDBInstance.ReplicationLoader.IncomingRejectionStats.FirstOrDefault().Value?.FirstOrDefault()?.Reason}");
                }
        }
Пример #9
0
        public async Task ChangeCertificateTypeInPullReplication()
        {
            var hubSettings  = new ConcurrentDictionary <string, string>();
            var sinkSettings = new ConcurrentDictionary <string, string>();

            var hubCertificates = Certificates.GenerateAndSaveSelfSignedCertificate();

            Certificates.SetupServerAuthentication(hubSettings, certificates: hubCertificates);

            var sinkCertificates = Certificates.GenerateAndSaveSelfSignedCertificate();
            var sinkCerts        = Certificates.SetupServerAuthentication(sinkSettings, certificates: sinkCertificates);

            var hubDB  = GetDatabaseName();
            var sinkDB = GetDatabaseName();
            var pullReplicationName = $"{hubDB}-pull";

            var hubServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = hubSettings, RegisterForDisposal = true
            });
            var sinkServer = GetNewServer(new ServerCreationOptions {
                CustomSettings = sinkSettings, RegisterForDisposal = true
            });

            var ownCertificate = new X509Certificate2(sinkCertificates.ClientCertificate1Path, (string)null, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

            Assert.True(ownCertificate.HasPrivateKey);

            using (var hubStore = GetDocumentStore(new Options
            {
                ClientCertificate = sinkCerts.ServerCertificate.Value,
                Server = hubServer,
                ModifyDatabaseName = _ => hubDB
            }))
                using (var sinkStore = GetDocumentStore(new Options
                {
                    ClientCertificate = sinkCerts.ServerCertificate.Value,
                    Server = sinkServer,
                    ModifyDatabaseName = _ => sinkDB
                }))
                {
                    await hubStore.Maintenance.SendAsync(new PutPullReplicationAsHubOperation(new PullReplicationDefinition(pullReplicationName)
                    {
#pragma warning disable CS0618 // Type or member is obsolete
                        Certificates = new Dictionary <string, string>
#pragma warning restore CS0618 // Type or member is obsolete
                        {
                            [sinkCerts.ServerCertificate.Value.Thumbprint] = Convert.ToBase64String(ownCertificate.Export(X509ContentType.Cert))
                        }
                    }));

                    await sinkStore.Maintenance.SendAsync(new PutConnectionStringOperation <RavenConnectionString>(new RavenConnectionString
                    {
                        Database = hubStore.Database,
                        Name = $"ConnectionString-{hubStore.Database}",
                        TopologyDiscoveryUrls = hubStore.Urls
                    }));

                    long taskId;
                    // sink with ownCertificate - unauthorized to access
                    var json = "{\"PullReplicationAsSink\":{\"TaskId\": null,\"Database\":\"" + hubStore.Database + "\",\"ConnectionStringName\": \"ConnectionString-" + hubStore.Database + "\",\"HubName\": \"" + pullReplicationName + "\",\"Mode\": \"HubToSink\",\"AccessName\": null,\"CertificateWithPrivateKey\":\" " + Convert.ToBase64String(ownCertificate.Export(X509ContentType.Pfx)) + "\",  \"AllowedHubToSinkPaths\": null ,\"AllowedSinkToHubPaths\": null }}";

                    using (var ctx = JsonOperationContext.ShortTermSingleUse())
                    {
                        BlittableJsonReaderObject reader = ctx.Sync.ReadForMemory(new MemoryStream(Encoding.UTF8.GetBytes(json)), "users/1");

                        using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                        {
                            var task = sinkServer.ServerStore.UpdatePullReplicationAsSink(sinkStore.Database, reader, Guid.NewGuid().ToString(), out PullReplicationAsSink pullReplication);
                            Task.WaitAll(task);
                            taskId = task.Result.Index;
                            Assert.NotNull(pullReplication.CertificateWithPrivateKey);
                        }
                    }

                    using (var hubSession = hubStore.OpenSession())
                    {
                        hubSession.Store(new User(), "foo/bar");
                        hubSession.SaveChanges();
                    }

                    Assert.False(WaitForDocument(sinkStore, "foo/bar", 3000));

                    // sink with null certificate => use server certificate - authorized
                    json = "{\"PullReplicationAsSink\":{\"TaskId\": \"" + taskId + "\",\"Database\":\"" + hubStore.Database + "\",\"ConnectionStringName\": \"ConnectionString-" + hubStore.Database + "\",\"HubName\": \"" + pullReplicationName + "\",\"Mode\": \"HubToSink\",\"AccessName\": null,\"CertificateWithPrivateKey\": null,  \"AllowedHubToSinkPaths\": null ,\"AllowedSinkToHubPaths\": null }}";

                    using (var ctx = JsonOperationContext.ShortTermSingleUse())
                    {
                        BlittableJsonReaderObject reader = ctx.Sync.ReadForMemory(new MemoryStream(Encoding.UTF8.GetBytes(json)), "users/1");

                        using (Server.ServerStore.ContextPool.AllocateOperationContext(out JsonOperationContext context))
                        {
                            var task = sinkServer.ServerStore.UpdatePullReplicationAsSink(sinkStore.Database, reader, Guid.NewGuid().ToString(), out PullReplicationAsSink pullReplication);
                            Task.WaitAll(task);
                            Assert.Null(pullReplication.CertificateWithPrivateKey);
                        }
                    }

                    using (var hubSession = hubStore.OpenSession())
                    {
                        hubSession.Store(new User(), "foo/bar2");
                        hubSession.SaveChanges();
                    }

                    Assert.True(WaitForDocument(sinkStore, "foo/bar2", 3000));
                }
        }