public void Update_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Re-insert the same node, nothing should have changed
            tree.AddOrUpdate(this.e2_L2).Wait();
            this.CheckValidAuthChains(tree);

            // Re-parent e3_L2 from e2_L1 to e1_L1
            ServiceIdentity updatedIdentity = CreateServiceIdentity(
                this.e3_L2.DeviceId,
                null,
                this.e3_L2.DeviceScope.Expect(() => new InvalidOperationException()),
                this.e1_L1.DeviceScope.Expect(() => new InvalidOperationException()),
                true);

            tree.AddOrUpdate(updatedIdentity).Wait();

            // Equality check
            Option <ServiceIdentity> roundTripIdentity = tree.Get(updatedIdentity.Id).Result;

            Assert.True(roundTripIdentity.Contains(updatedIdentity));

            // The child of e3_L2, leaf2, should also go through a different path for authchain now
            Option <string> authChainActual          = tree.GetAuthChain(this.leaf2.Id).Result;
            string          leaf2_authchain_expected =
                this.leaf2.Id + ";" +
                this.e3_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf2_authchain_expected));
        }
        public void Remove_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Delete a node
            tree.Remove(this.e2_L1.Id).Wait();

            // Auth-chains for everything in its subtree should be invalidated
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod2.Id).Result.HasValue);

            // Delete the rest of the subtree
            tree.Remove(this.e3_L2.Id).Wait();
            tree.Remove(this.e4_L2.Id).Wait();
            tree.Remove(this.leaf2.Id).Wait();
            tree.Remove(this.mod2.Id).Wait();

            // Nothing under e2_L1 should remain
            Assert.False(tree.Get(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.Get(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.Get(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.Get(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.Get(this.mod2.Id).Result.HasValue);
        }
        public void Insertion_OutOfOrder_Test()
        {
            var tree = new ServiceIdentityTree(this.root.Id);

            // Insert L2 identities
            tree.AddOrUpdate(this.e1_L2).Wait();
            tree.AddOrUpdate(this.e2_L2).Wait();
            tree.AddOrUpdate(this.e3_L2).Wait();
            tree.AddOrUpdate(this.e4_L2).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);

            // Insert L1 identities
            tree.AddOrUpdate(this.e1_L1).Wait();
            tree.AddOrUpdate(this.e2_L1).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e1_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);

            // Insert leaf identities
            tree.AddOrUpdate(this.leaf1).Wait();
            tree.AddOrUpdate(this.leaf2).Wait();
            tree.AddOrUpdate(this.mod1).Wait();
            tree.AddOrUpdate(this.mod2).Wait();

            // Should have no valid auth chains
            Assert.False(tree.GetAuthChain(this.e1_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e3_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e4_L2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e1_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.e2_L1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.leaf2.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod1.Id).Result.HasValue);
            Assert.False(tree.GetAuthChain(this.mod2.Id).Result.HasValue);

            // Insert root
            tree.AddOrUpdate(this.root).Wait();

            // All auth chains should now be valid because root is available
            this.CheckValidAuthChains(tree);
        }
        public void GetAuthChain_DisabledDevice_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Add another branch with a disabled Edge
            ServiceIdentity edge_L2 = CreateServiceIdentity("edge_L2", null, "edge_L2_scope", "e1_L1_scope", true, false);
            ServiceIdentity leaf    = CreateServiceIdentity("leaf", null, null, "edge_L2_scope", false);

            tree.AddOrUpdate(edge_L2).Wait();
            tree.AddOrUpdate(leaf).Wait();

            // Act
            Option <string> authChain = tree.GetAuthChain(leaf.Id).Result;

            // Assert
            Assert.False(authChain.HasValue);
        }
        public void GetAuthChain_Test()
        {
            // Setup our tree
            ServiceIdentityTree tree = this.SetupTree();

            // Check for valid auth chains
            this.CheckValidAuthChains(tree);

            // Check non-existent auth chain
            Assert.False(tree.GetAuthChain("nonexistent").Result.HasValue);

            // Insert an orphaned node and check for its invalid auth chain
            ServiceIdentity orphan = CreateServiceIdentity("orphan", null, null, null, false);

            tree.AddOrUpdate(orphan).Wait();
            Assert.False(tree.GetAuthChain(orphan.Id).Result.HasValue);
        }
        internal ServiceIdentityTree SetupTree()
        {
            var tree = new ServiceIdentityTree(this.root.Id);

            tree.AddOrUpdate(this.root).Wait();
            tree.AddOrUpdate(this.e1_L1).Wait();
            tree.AddOrUpdate(this.e2_L1).Wait();
            tree.AddOrUpdate(this.e1_L2).Wait();
            tree.AddOrUpdate(this.e2_L2).Wait();
            tree.AddOrUpdate(this.e3_L2).Wait();
            tree.AddOrUpdate(this.e4_L2).Wait();
            tree.AddOrUpdate(this.leaf1).Wait();
            tree.AddOrUpdate(this.leaf2).Wait();
            tree.AddOrUpdate(this.mod1).Wait();
            tree.AddOrUpdate(this.mod2).Wait();

            return(tree);
        }
        public async Task GetAllIdsTest()
        {
            // Arrage
            ServiceIdentityTree tree = this.SetupTree();
            IList <string>      identitiesExpected = new List <string>()
            {
                this.root.Id, this.e1_L1.Id, this.e1_L2.Id, this.e2_L1.Id, this.e2_L2.Id, this.e3_L2.Id, this.e4_L2.Id, this.leaf1.Id, this.leaf2.Id, this.mod1.Id, this.mod2.Id
            };

            // Act
            IList <string> identities = await tree.GetAllIds();

            // Assert
            Assert.Equal(11, identities.Count);
            foreach (string id in identitiesExpected)
            {
                Assert.Contains(id, identities);
            }
        }
        public async Task TryGetAuthChain_DisabledDevice_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Add another branch with a disabled Edge
            ServiceIdentity edge_L2           = CreateServiceIdentity("edge_L2", null, "edge_L2_scope", "e1_L1_scope", true, false);
            ServiceIdentity leaf              = CreateServiceIdentity("leaf", null, null, "edge_L2_scope", false);
            var             expectedAuthChain = "leaf;edge_L2;e1_L1;root";

            tree.AddOrUpdate(edge_L2).Wait();
            tree.AddOrUpdate(leaf).Wait();

            // Act
            var authChain = await tree.TryGetAuthChain(leaf.Id);

            // Assert
            Assert.True(authChain.Success);
            Assert.Equal(expectedAuthChain, authChain.Value);
        }
        internal void CheckValidAuthChains(ServiceIdentityTree tree)
        {
            // Check leaf1
            Option <string> authChainActual          = tree.GetAuthChain(this.leaf1.Id).Result;
            string          leaf1_authchain_expected =
                this.leaf1.Id + ";" +
                this.e1_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf1_authchain_expected));

            // Check leaf2
            authChainActual = tree.GetAuthChain(this.leaf2.Id).Result;
            string leaf2_authchain_expected =
                this.leaf2.Id + ";" +
                this.e3_L2.Id + ";" +
                this.e2_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf2_authchain_expected));

            // Check mod1
            authChainActual = tree.GetAuthChain(this.mod1.Id).Result;
            string mod1_authchain_expected =
                this.mod1.Id + ";" +
                this.e2_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(mod1_authchain_expected));

            // Check mod2
            authChainActual = tree.GetAuthChain(this.mod2.Id).Result;
            string mod2_authchain_expected =
                this.mod2.Id + ";" +
                this.e4_L2.Id + ";" +
                this.e2_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(mod2_authchain_expected));
        }
Example #10
0
        public async Task TryGetAuthChain_Test()
        {
            // Setup our tree
            ServiceIdentityTree tree = this.SetupTree();

            // Check for valid auth chains
            this.CheckValidAuthChains(tree);

            // Check non-existent auth chain
            var authChainTry = await tree.TryGetAuthChain("nonexistent");

            Assert.Throws <DeviceInvalidStateException>(() => authChainTry.Value);

            // Insert an orphaned node and check for its invalid auth chain
            ServiceIdentity orphan = CreateServiceIdentity("orphan", null, null, null, false);

            tree.AddOrUpdate(orphan).Wait();
            authChainTry = await tree.TryGetAuthChain(orphan.Id);

            Assert.Throws <DeviceInvalidStateException>(() => authChainTry.Value);
        }
Example #11
0
        public void MaxDepth_test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            // Create an orphaned chain
            ServiceIdentity e1_L3 = CreateServiceIdentity("e1_L3", null, "e1_L3_scope", null, true);
            ServiceIdentity e1_L4 = CreateServiceIdentity("e1_L4", null, "e1_L4_scope", "e1_L3_scope", true);
            ServiceIdentity e1_L5 = CreateServiceIdentity("e1_L5", null, "e1_L5_scope", "e1_L4_scope", true);

            tree.AddOrUpdate(e1_L3).Wait();
            tree.AddOrUpdate(e1_L4).Wait();
            tree.AddOrUpdate(e1_L5).Wait();

            // Merge this chain into the main tree, this exceeds the maximum depth,
            // and e1_L5 should have no valid auth chain
            e1_L3 = CreateServiceIdentity("e1_L3", null, "e1_L3_scope", "e1_L2_scope", true);
            tree.AddOrUpdate(e1_L3).Wait();
            Assert.False(tree.GetAuthChain(e1_L5.Id).Result.HasValue);

            // Try explicitly adding yet another layer with an Edge device, this shouldn't yield a valid chain
            tree.AddOrUpdate(e1_L5).Wait();
            Assert.False(tree.GetAuthChain(e1_L5.Id).Result.HasValue);

            // But we should still be able to add a leaf device
            ServiceIdentity leaf = CreateServiceIdentity("leaf", null, null, "e1_L4_scope", false);

            tree.AddOrUpdate(leaf).Wait();

            Option <string> authChainActual         = tree.GetAuthChain(leaf.Id).Result;
            string          leaf_authchain_expected =
                leaf.Id + ";" +
                e1_L4.Id + ";" +
                e1_L3.Id + ";" +
                this.e1_L2.Id + ";" +
                this.e1_L1.Id + ";" +
                this.root.Id;

            Assert.True(authChainActual.Contains(leaf_authchain_expected));
        }
Example #12
0
        public void Update_NotChanged_Test()
        {
            ServiceIdentityTree tree = this.SetupTree();

            ServiceIdentity updated = CreateServiceIdentity("e2_L2", null, "e2_L2_scope", "e1_L1_scope", true);
            ServiceIdentity root    = CreateServiceIdentity("root", null, "rootScope", null, true);

            // Re-insert the same node, nothing should have changed
            tree.AddOrUpdate(updated).Wait();
            tree.AddOrUpdate(root).Wait();
            this.CheckValidAuthChains(tree);

            Option <ServiceIdentity> roundTripIdentity = tree.Get(this.e2_L2.Id).Result;
            Option <ServiceIdentity> roundTripRoot     = tree.Get(this.root.Id).Result;

            Assert.True(roundTripIdentity.HasValue);
            Assert.True(ReferenceEquals(roundTripIdentity.OrDefault(), this.e2_L2));
            Assert.False(ReferenceEquals(roundTripIdentity.OrDefault(), updated));
            Assert.True(roundTripRoot.HasValue);
            Assert.True(ReferenceEquals(roundTripRoot.OrDefault(), this.root));
            Assert.False(ReferenceEquals(roundTripRoot.OrDefault(), root));
        }
Example #13
0
        protected override void Load(ContainerBuilder builder)
        {
            // IMetricsListener
            builder.Register(
                c =>
                this.metricsConfig.Enabled
                            ? new MetricsListener(this.metricsConfig.ListenerConfig, c.Resolve <IMetricsProvider>())
                            : new NullMetricsListener() as IMetricsListener)
            .As <IMetricsListener>()
            .SingleInstance();

            // IMetricsProvider
            builder.Register(
                c =>
                this.metricsConfig.Enabled
                            ? new MetricsProvider(MetricsConstants.EdgeHubMetricPrefix, this.iothubHostName, this.edgeDeviceId, this.metricsConfig.HistogramMaxAge)
                            : new NullMetricsProvider() as IMetricsProvider)
            .As <IMetricsProvider>()
            .SingleInstance();

            // ISignatureProvider
            builder.Register(
                c =>
            {
                ISignatureProvider signatureProvider = this.edgeHubConnectionString.Map(
                    cs =>
                {
                    IotHubConnectionStringBuilder csBuilder = IotHubConnectionStringBuilder.Create(cs);
                    return(new SharedAccessKeySignatureProvider(csBuilder.SharedAccessKey) as ISignatureProvider);
                })
                                                       .GetOrElse(
                    () =>
                {
                    string edgeHubGenerationId = this.edgeHubGenerationId.Expect(() => new InvalidOperationException("Generation ID missing"));
                    string workloadUri         = this.workloadUri.Expect(() => new InvalidOperationException("workloadUri is missing"));
                    string workloadApiVersion  = this.workloadApiVersion.Expect(() => new InvalidOperationException("workloadUri version is missing"));
                    return(new HttpHsmSignatureProvider(this.edgeHubModuleId, edgeHubGenerationId, workloadUri, workloadApiVersion, Constants.WorkloadApiVersion) as ISignatureProvider);
                });
                return(signatureProvider);
            })
            .As <ISignatureProvider>()
            .SingleInstance();

            // Detect system environment
            builder.Register(c => new SystemEnvironment())
            .As <ISystemEnvironment>()
            .SingleInstance();

            // DataBase options
            builder.Register(c => new RocksDbOptionsProvider(c.Resolve <ISystemEnvironment>(), this.optimizeForPerformance, this.storageMaxTotalWalSize, this.storageMaxOpenFiles, this.storageLogLevel))
            .As <IRocksDbOptionsProvider>()
            .SingleInstance();

            if (!this.usePersistentStorage && this.useBackupAndRestore)
            {
                // Backup and restore serialization
                builder.Register(c => new ProtoBufDataBackupRestore())
                .As <IDataBackupRestore>()
                .SingleInstance();
            }

            // IStorageSpaceChecker
            builder.Register(
                c =>
            {
                IStorageSpaceChecker spaceChecker = !this.usePersistentStorage
                       ? new MemorySpaceChecker(() => 0L) as IStorageSpaceChecker
                       : new NullStorageSpaceChecker();
                return(spaceChecker);
            })
            .As <IStorageSpaceChecker>()
            .SingleInstance();

            // IDbStoreProvider
            builder.Register(
                async c =>
            {
                var loggerFactory = c.Resolve <ILoggerFactory>();
                ILogger logger    = loggerFactory.CreateLogger(typeof(RoutingModule));

                if (this.usePersistentStorage)
                {
                    // Create partitions for messages and twins
                    var partitionsList = new List <string> {
                        Core.Constants.MessageStorePartitionKey, Core.Constants.TwinStorePartitionKey, Core.Constants.CheckpointStorePartitionKey
                    };
                    try
                    {
                        IDbStoreProvider dbStoreProvider = DbStoreProvider.Create(
                            c.Resolve <IRocksDbOptionsProvider>(),
                            this.storagePath,
                            partitionsList);
                        logger.LogInformation($"Created persistent store at {this.storagePath}");
                        return(dbStoreProvider);
                    }
                    catch (Exception ex) when(!ex.IsFatal())
                    {
                        logger.LogError(ex, "Error creating RocksDB store. Falling back to in-memory store.");
                        IDbStoreProvider dbStoreProvider = await this.BuildInMemoryDbStoreProvider(c);
                        return(dbStoreProvider);
                    }
                }
                else
                {
                    logger.LogInformation($"Using in-memory store");
                    IDbStoreProvider dbStoreProvider = await this.BuildInMemoryDbStoreProvider(c);
                    return(dbStoreProvider);
                }
            })
            .As <Task <IDbStoreProvider> >()
            .SingleInstance();

            // Task<Option<IEncryptionProvider>>
            builder.Register(
                async c =>
            {
                Option <IEncryptionProvider> encryptionProviderOption = await this.workloadUri
                                                                        .Map(
                    async uri =>
                {
                    var encryptionProvider = await EncryptionProvider.CreateAsync(
                        this.storagePath,
                        new Uri(uri),
                        this.workloadApiVersion.Expect(() => new InvalidOperationException("Missing workload API version")),
                        Constants.WorkloadApiVersion,
                        this.edgeHubModuleId,
                        this.edgeHubGenerationId.Expect(() => new InvalidOperationException("Missing generation ID")),
                        Constants.InitializationVectorFileName) as IEncryptionProvider;
                    return(Option.Some(encryptionProvider));
                })
                                                                        .GetOrElse(() => Task.FromResult(Option.None <IEncryptionProvider>()));
                return(encryptionProviderOption);
            })
            .As <Task <Option <IEncryptionProvider> > >()
            .SingleInstance();

            // Task<IStoreProvider>
            builder.Register(async c =>
            {
                var dbStoreProvider          = await c.Resolve <Task <IDbStoreProvider> >();
                IStoreProvider storeProvider = new StoreProvider(dbStoreProvider);
                return(storeProvider);
            })
            .As <Task <IStoreProvider> >()
            .SingleInstance();

            // Task<IMetadataStore>
            builder.Register(
                async c =>
            {
                var storeProvider = await c.Resolve <Task <IStoreProvider> >();
                IKeyValueStore <string, string> entityStore = storeProvider.GetEntityStore <string, string>("ProductInfo", "MetadataStore");
                IMetadataStore metadataStore = new MetadataStore(entityStore, this.productInfo);
                return(metadataStore);
            })
            .As <Task <IMetadataStore> >()
            .SingleInstance();

            // ITokenProvider
            builder.Register(c => new ClientTokenProvider(c.Resolve <ISignatureProvider>(), this.iothubHostName, this.edgeDeviceId, this.edgeHubModuleId, TimeSpan.FromHours(1)))
            .Named <ITokenProvider>("EdgeHubClientAuthTokenProvider")
            .SingleInstance();

            // ITokenProvider
            builder.Register(
                c =>
            {
                string deviceId = WebUtility.UrlEncode(this.edgeDeviceId);
                string moduleId = WebUtility.UrlEncode(this.edgeHubModuleId);
                return(new ClientTokenProvider(c.Resolve <ISignatureProvider>(), this.iothubHostName, deviceId, moduleId, TimeSpan.FromHours(1)));
            })
            .Named <ITokenProvider>("EdgeHubServiceAuthTokenProvider")
            .SingleInstance();

            builder.Register(
                c =>
            {
                var loggerFactory = c.Resolve <ILoggerFactory>();
                var logger        = loggerFactory.CreateLogger <RoutingModule>();
                return(Proxy.Parse(this.proxy, logger));
            })
            .As <Option <IWebProxy> >()
            .SingleInstance();

            // Task<IDeviceScopeIdentitiesCache>
            builder.Register(
                async c =>
            {
                IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache;
                if (this.authenticationMode == AuthenticationMode.CloudAndScope || this.authenticationMode == AuthenticationMode.Scope)
                {
                    var edgeHubTokenProvider = c.ResolveNamed <ITokenProvider>("EdgeHubServiceAuthTokenProvider");
                    var proxy = c.Resolve <Option <IWebProxy> >();

                    IServiceIdentityHierarchy serviceIdentityHierarchy;
                    if (this.nestedEdgeEnabled)
                    {
                        serviceIdentityHierarchy = new ServiceIdentityTree(this.edgeDeviceId);
                    }
                    else
                    {
                        serviceIdentityHierarchy = new ServiceIdentityDictionary(this.edgeDeviceId);
                    }

                    string hostName = this.gatewayHostName.GetOrElse(this.iothubHostName);
                    IDeviceScopeApiClientProvider securityScopesApiClientProvider = new DeviceScopeApiClientProvider(hostName, this.edgeDeviceId, this.edgeHubModuleId, 10, edgeHubTokenProvider, serviceIdentityHierarchy, proxy);
                    IServiceProxy serviceProxy = new ServiceProxy(securityScopesApiClientProvider, this.nestedEdgeEnabled);
                    IKeyValueStore <string, string> encryptedStore = await GetEncryptedStore(c, "DeviceScopeCache");
                    deviceScopeIdentitiesCache = await DeviceScopeIdentitiesCache.Create(serviceIdentityHierarchy, serviceProxy, encryptedStore, this.scopeCacheRefreshRate, this.scopeCacheRefreshDelay);
                }
                else
                {
                    deviceScopeIdentitiesCache = new NullDeviceScopeIdentitiesCache();
                }

                return(deviceScopeIdentitiesCache);
            })
            .As <Task <IDeviceScopeIdentitiesCache> >()
            .AutoActivate()
            .SingleInstance();

            // IRegistryApiClient
            builder.Register(
                c =>
            {
                string upstreamHostname = this.gatewayHostName.GetOrElse(this.iothubHostName);
                var proxy = c.Resolve <Option <IWebProxy> >();
                var edgeHubTokenProvider = c.ResolveNamed <ITokenProvider>("EdgeHubServiceAuthTokenProvider");
                return(new RegistryOnBehalfOfApiClient(upstreamHostname, proxy, edgeHubTokenProvider));
            })
            .As <IRegistryOnBehalfOfApiClient>()
            .SingleInstance();

            // Task<ICredentialsCache>
            builder.Register(
                async c =>
            {
                ICredentialsCache underlyingCredentialsCache;
                if (this.persistTokens)
                {
                    IKeyValueStore <string, string> encryptedStore = await GetEncryptedStore(c, "CredentialsCache");
                    return(new PersistedTokenCredentialsCache(encryptedStore));
                }
                else
                {
                    underlyingCredentialsCache = new NullCredentialsCache();
                }

                ICredentialsCache credentialsCache;

                if (this.nestedEdgeEnabled)
                {
                    credentialsCache = new NestedCredentialsCache(underlyingCredentialsCache);
                }
                else
                {
                    credentialsCache = new CredentialsCache(underlyingCredentialsCache);
                }

                return(credentialsCache);
            })
            .As <Task <ICredentialsCache> >()
            .SingleInstance();

            // Task<IAuthenticator>
            builder.Register(
                async c =>
            {
                IAuthenticator tokenAuthenticator;
                IAuthenticator certificateAuthenticator;
                IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache;
                var credentialsCacheTask = c.Resolve <Task <ICredentialsCache> >();
                // by default regardless of how the authenticationMode, X.509 certificate validation will always be scoped
                deviceScopeIdentitiesCache = await c.Resolve <Task <IDeviceScopeIdentitiesCache> >();
                certificateAuthenticator   = new DeviceScopeCertificateAuthenticator(deviceScopeIdentitiesCache, new NullAuthenticator(), this.trustBundle, true, this.nestedEdgeEnabled);
                switch (this.authenticationMode)
                {
                case AuthenticationMode.Cloud:
                    tokenAuthenticator = await this.GetCloudTokenAuthenticator(c);
                    break;

                case AuthenticationMode.Scope:
                    tokenAuthenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdentitiesCache, this.iothubHostName, this.edgeDeviceHostName, new NullAuthenticator(), true, true, this.nestedEdgeEnabled);
                    break;

                default:
                    IAuthenticator cloudTokenAuthenticator = await this.GetCloudTokenAuthenticator(c);
                    tokenAuthenticator = new DeviceScopeTokenAuthenticator(deviceScopeIdentitiesCache, this.iothubHostName, this.edgeDeviceHostName, cloudTokenAuthenticator, true, true, this.nestedEdgeEnabled);
                    break;
                }

                ICredentialsCache credentialsCache = await credentialsCacheTask;
                return(new Authenticator(tokenAuthenticator, certificateAuthenticator, credentialsCache) as IAuthenticator);
            })
            .As <Task <IAuthenticator> >()
            .SingleInstance();

            // IClientCredentialsFactory
            builder.Register(c => new ClientCredentialsFactory(c.Resolve <IIdentityProvider>(), this.productInfo))
            .As <IClientCredentialsFactory>()
            .SingleInstance();

            // ConnectionReauthenticator
            builder.Register(
                async c =>
            {
                var edgeHubCredentials               = c.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
                var connectionManagerTask            = c.Resolve <Task <IConnectionManager> >();
                var authenticatorTask                = c.Resolve <Task <IAuthenticator> >();
                var credentialsCacheTask             = c.Resolve <Task <ICredentialsCache> >();
                var deviceScopeIdentitiesCacheTask   = c.Resolve <Task <IDeviceScopeIdentitiesCache> >();
                var deviceConnectivityManager        = c.Resolve <IDeviceConnectivityManager>();
                IConnectionManager connectionManager = await connectionManagerTask;
                IAuthenticator authenticator         = await authenticatorTask;
                ICredentialsCache credentialsCache   = await credentialsCacheTask;
                IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await deviceScopeIdentitiesCacheTask;
                var connectionReauthenticator = new ConnectionReauthenticator(
                    connectionManager,
                    authenticator,
                    credentialsCache,
                    deviceScopeIdentitiesCache,
                    TimeSpan.FromMinutes(5),
                    edgeHubCredentials.Identity,
                    deviceConnectivityManager);
                return(connectionReauthenticator);
            })
            .As <Task <ConnectionReauthenticator> >()
            .SingleInstance();

            base.Load(builder);
        }