コード例 #1
0
    public void CheckSingleValuePlusMatchWithPlus()
    {
        var result = TopicChecker.Regex("a/+", "a/+");

        Assert.IsTrue(result);
    }
コード例 #2
0
        public void CheckMultipleValuePlusMatchNoOperators()
        {
            var result = TopicChecker.Regex("a/+/+/a", "a/b/b/a");

            Assert.IsTrue(result);
        }
コード例 #3
0
    public void CheckSingleValuePlusDontMatchWithCross()
    {
        var result = TopicChecker.Regex("a/+", "a/#");

        Assert.IsFalse(result);
    }
コード例 #4
0
        public void CheckMultipleValueCrossMatchMixed()
        {
            var result = TopicChecker.Regex("a/#", "a/+/a/#");

            Assert.IsTrue(result);
        }
コード例 #5
0
        public void CheckMultipleValueCrossDontMatchMixed()
        {
            var result = TopicChecker.Regex("a/#", "a/+/+/#/a");

            Assert.IsFalse(result);
        }
コード例 #6
0
        /// <inheritdoc cref="IMqttValidator"/>
        /// <summary>
        ///     Validates the subscription.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="blacklist">The blacklist.</param>
        /// <param name="whitelist">The whitelist.</param>
        /// <param name="user">The user.</param>
        /// <param name="clientIdPrefixes">The client identifier prefixes.</param>
        /// <returns>A value indicating whether the subscription is accepted or not.</returns>
        /// <seealso cref="IMqttValidator"/>
        public bool ValidateSubscription(
            MqttSubscriptionInterceptorContext context,
            List <BlacklistWhitelist> blacklist,
            List <BlacklistWhitelist> whitelist,
            User user,
            List <string> clientIdPrefixes)
        {
            Logger.Debug("Executed ValidateSubscription with parameters: {@context}, {@user}.", context, user);

            var clientIdPrefix = GetClientIdPrefix(context.ClientId, clientIdPrefixes);

            Logger.Debug("Client id prefix is {@clientIdPrefix}.", clientIdPrefix);

            if (user == null)
            {
                Logger.Debug("Current user was null.");
                return(false);
            }

            var topic = context.TopicFilter.Topic;

            Logger.Debug("Topic was {@topic}.", topic);
            Logger.Debug("The blacklist was {@blacklist}.", blacklist);
            Logger.Debug("The whitelist was {@whitelist}.", whitelist);

            // Check matches
            if (blacklist.Any(b => b.Value == topic))
            {
                Logger.Debug("The blacklist matched a topic.");
                return(false);
            }

            if (whitelist.Any(b => b.Value == topic))
            {
                Logger.Debug("The whitelist matched a topic.");
                return(true);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var forbiddenTopic in blacklist)
            {
                var doesTopicMatch = TopicChecker.Regex(forbiddenTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Debug("The blacklist matched a topic with regex.");
                return(false);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var allowedTopic in whitelist)
            {
                var doesTopicMatch = TopicChecker.Regex(allowedTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Debug("The whitelist matched a topic with regex.");
                return(true);
            }

            Logger.Warning(
                "We fell through everything else. This should never happen! Context was {@context}.",
                context);
            return(false);
        }
        public void CheckSingleValueCrossMatchWithCross()
        {
            var result = TopicChecker.Regex("a/#", "a/#");

            Assert.IsTrue(result);
        }
コード例 #8
0
        /// <inheritdoc cref="IMqttValidator"/>
        /// <summary>
        ///     Validates the message publication.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="blacklist">The blacklist.</param>
        /// <param name="whitelist">The whitelist.</param>
        /// <param name="user">The user.</param>
        /// <param name="dataLimitCacheMonth">The data limit cache for the month.</param>
        /// <param name="clientIdPrefixes">The client identifier prefixes.</param>
        /// <returns>A value indicating whether the published message is accepted or not.</returns>
        /// <seealso cref="IMqttValidator"/>
        public bool ValidatePublish(
            MqttApplicationMessageInterceptorContext context,
            List <BlacklistWhitelist> blacklist,
            List <BlacklistWhitelist> whitelist,
            User user,
            IMemoryCache dataLimitCacheMonth,
            List <string> clientIdPrefixes)
        {
            Logger.Debug("Executed ValidatePublish with parameters: {@context}, {@user}.", context, user);

            var clientIdPrefix = GetClientIdPrefix(context.ClientId, clientIdPrefixes);

            Logger.Debug("Client id prefix is {@clientIdPrefix}.", clientIdPrefix);

            if (user == null)
            {
                Logger.Debug("Current user was null.");
                return(false);
            }

            var topic = context.ApplicationMessage.Topic;

            Logger.Debug("Topic was {@topic}.", topic);

            if (user.ThrottleUser)
            {
                var payload = context.ApplicationMessage?.Payload;

                if (payload != null)
                {
                    if (user.MonthlyByteLimit != null)
                    {
                        if (IsUserThrottled(
                                context.ClientId,
                                payload.Length,
                                user.MonthlyByteLimit.Value,
                                dataLimitCacheMonth))
                        {
                            Logger.Debug("User is throttled now.");
                            return(false);
                        }
                    }
                }
            }

            Logger.Debug("The blacklist was {@blacklist}.", blacklist);
            Logger.Debug("The whitelist was {@whitelist}.", whitelist);

            // Check matches
            if (blacklist.Any(b => b.Value == topic))
            {
                Logger.Debug("The blacklist matched a topic.");
                return(false);
            }

            if (whitelist.Any(b => b.Value == topic))
            {
                Logger.Debug("The whitelist matched a topic.");
                return(true);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var forbiddenTopic in blacklist)
            {
                var doesTopicMatch = TopicChecker.Regex(forbiddenTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Debug("The blacklist matched a topic with regex.");
                return(false);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var allowedTopic in whitelist)
            {
                var doesTopicMatch = TopicChecker.Regex(allowedTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Debug("The whitelist matched a topic with regex.");
                return(true);
            }

            Logger.Warning("We fell through everything else. This should never happen! Context was {@context}.", context);
            return(false);
        }
コード例 #9
0
        /// <summary>
        ///     Validates the subscription.
        /// </summary>
        /// <param name="context">The context.</param>
        private async void ValidateSubscription(MqttSubscriptionInterceptorContext context)
        {
            var clientIdPrefix = await this.GetClientIdPrefix(context.ClientId);

            User currentUser;
            bool userFound;

            if (string.IsNullOrWhiteSpace(clientIdPrefix))
            {
                userFound   = context.SessionItems.TryGetValue(context.ClientId, out var currentUserObject);
                currentUser = currentUserObject as User;
            }
            else
            {
                userFound   = context.SessionItems.TryGetValue(clientIdPrefix, out var currentUserObject);
                currentUser = currentUserObject as User;
            }

            if (!userFound || currentUser == null)
            {
                context.AcceptSubscription = false;
                LogMessage(context, false);
                return;
            }

            var topic = context.TopicFilter.Topic;

            // Get blacklist
            var publishBlackList = await this.userRepository.GetBlacklistItemsForUser(currentUser.Id, BlacklistWhitelistType.Subscribe);

            var blacklist = publishBlackList?.ToList() ?? new List <BlacklistWhitelist>();

            // Get whitelist
            var publishWhitelist = await this.userRepository.GetWhitelistItemsForUser(currentUser.Id, BlacklistWhitelistType.Subscribe);

            var whitelist = publishWhitelist?.ToList() ?? new List <BlacklistWhitelist>();

            // Check matches
            if (blacklist.Any(b => b.Value == topic))
            {
                context.AcceptSubscription = false;
                LogMessage(context, false);
                return;
            }

            if (whitelist.Any(b => b.Value == topic))
            {
                context.AcceptSubscription = true;
                LogMessage(context, true);
                return;
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var forbiddenTopic in blacklist)
            {
                var doesTopicMatch = TopicChecker.Regex(forbiddenTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                context.AcceptSubscription = false;
                LogMessage(context, false);
                return;
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var allowedTopic in whitelist)
            {
                var doesTopicMatch = TopicChecker.Regex(allowedTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                context.AcceptSubscription = true;
                LogMessage(context, true);
                return;
            }

            context.AcceptSubscription = false;
            LogMessage(context, false);
        }
コード例 #10
0
        /// <summary>
        ///     Validates the message publication.
        /// </summary>
        /// <param name="context">The context.</param>
        private async void ValidatePublish(MqttApplicationMessageInterceptorContext context)
        {
            var clientIdPrefix = await this.GetClientIdPrefix(context.ClientId);

            User currentUser;
            bool userFound;

            if (string.IsNullOrWhiteSpace(clientIdPrefix))
            {
                userFound   = context.SessionItems.TryGetValue(context.ClientId, out var currentUserObject);
                currentUser = currentUserObject as User;
            }
            else
            {
                userFound   = context.SessionItems.TryGetValue(clientIdPrefix, out var currentUserObject);
                currentUser = currentUserObject as User;
            }

            if (!userFound || currentUser == null)
            {
                context.AcceptPublish = false;
                return;
            }

            var topic = context.ApplicationMessage.Topic;

            if (currentUser.ThrottleUser)
            {
                var payload = context.ApplicationMessage?.Payload;

                if (payload != null)
                {
                    if (currentUser.MonthlyByteLimit != null)
                    {
                        if (IsUserThrottled(context.ClientId, payload.Length, currentUser.MonthlyByteLimit.Value))
                        {
                            context.AcceptPublish = false;
                            return;
                        }
                    }
                }
            }

            // Get blacklist
            var publishBlackList = await this.userRepository.GetBlacklistItemsForUser(currentUser.Id, BlacklistWhitelistType.Publish);

            var blacklist = publishBlackList?.ToList() ?? new List <BlacklistWhitelist>();

            // Get whitelist
            var publishWhitelist = await this.userRepository.GetWhitelistItemsForUser(currentUser.Id, BlacklistWhitelistType.Publish);

            var whitelist = publishWhitelist?.ToList() ?? new List <BlacklistWhitelist>();

            // Check matches
            if (blacklist.Any(b => b.Value == topic))
            {
                context.AcceptPublish = false;
                return;
            }

            if (whitelist.Any(b => b.Value == topic))
            {
                context.AcceptPublish = true;
                LogMessage(context);
                return;
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var forbiddenTopic in blacklist)
            {
                var doesTopicMatch = TopicChecker.Regex(forbiddenTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                context.AcceptPublish = false;
                return;
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var allowedTopic in whitelist)
            {
                var doesTopicMatch = TopicChecker.Regex(allowedTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                context.AcceptPublish = true;
                LogMessage(context);
                return;
            }

            context.AcceptPublish = false;
        }
コード例 #11
0
        /// <inheritdoc cref="IMqttValidator"/>
        /// <summary>
        ///     Validates the subscription.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="userRepository">The user repository.</param>
        /// <param name="users">The users.</param>
        /// <returns>A value indicating whether the subscription is accepted or not.</returns>
        /// <seealso cref="IMqttValidator"/>
        public async Task <bool> ValidateSubscription(MqttSubscriptionInterceptorContext context, IUserRepository userRepository, IDictionary <string, User> users)
        {
            Logger.Information("Executed ValidateSubscription with parameters: {context}, {users}", context, users);

            var clientIdPrefix = await GetClientIdPrefix(context.ClientId, userRepository);

            Logger.Information("Client id prefix is {clientIdPrefix}.", clientIdPrefix);

            User currentUser;
            bool userFound;

            if (string.IsNullOrWhiteSpace(clientIdPrefix))
            {
                userFound = users.TryGetValue(context.ClientId, out var currentUserObject);

                // ReSharper disable once StyleCop.SA1126
                currentUser = currentUserObject;

                Logger.Information("User was found: {userFound}, Current user was {currentUser} when client id prefix was null.", userFound, currentUser);
            }
            else
            {
                userFound = users.TryGetValue(clientIdPrefix, out var currentUserObject);

                // ReSharper disable once StyleCop.SA1126
                currentUser = currentUserObject;

                Logger.Information("User was found: {userFound}, Current user was {currentUser} when client id prefix was not null.", userFound, currentUser);
            }

            if (currentUser == null)
            {
                Logger.Information("Current user was null.");
            }

            if (!userFound || currentUser == null)
            {
                return(false);
            }

            var topic = context.TopicFilter.Topic;

            Logger.Information("Topic was {topic}.", topic);

            // Get blacklist
            var subscriptionBlacklist = await userRepository.GetBlacklistItemsForUser(currentUser.Id, BlacklistWhitelistType.Subscribe);

            var blacklist = subscriptionBlacklist?.ToList() ?? new List <BlacklistWhitelist>();

            Logger.Information("The blacklist was {blacklist}.", blacklist);

            // Get whitelist
            var subscriptionWhitelist = await userRepository.GetWhitelistItemsForUser(currentUser.Id, BlacklistWhitelistType.Subscribe);

            var whitelist = subscriptionWhitelist?.ToList() ?? new List <BlacklistWhitelist>();

            Logger.Information("The whitelist was {whitelist}.", whitelist);

            // Check matches
            if (blacklist.Any(b => b.Value == topic))
            {
                Logger.Information("The blacklist matched a topic.");
                return(false);
            }

            if (whitelist.Any(b => b.Value == topic))
            {
                Logger.Information("The whitelist matched a topic.");
                return(true);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var forbiddenTopic in blacklist)
            {
                var doesTopicMatch = TopicChecker.Regex(forbiddenTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Information("The blacklist matched a topic with regex.");
                return(false);
            }

            // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator
            foreach (var allowedTopic in whitelist)
            {
                var doesTopicMatch = TopicChecker.Regex(allowedTopic.Value, topic);

                if (!doesTopicMatch)
                {
                    continue;
                }

                Logger.Information("The whitelist matched a topic with regex.");
                return(true);
            }

            Logger.Information("We fell through everything else. This should never happen!");
            return(false);
        }
コード例 #12
0
        /// <summary>
        ///     Configures the services.
        /// </summary>
        /// <param name="services">The services.</param>
        public void ConfigureServices(IServiceCollection services)
        {
            // Added the custom configuration options
            services.Configure <DatabaseConnectionSettings>(this.Configuration.GetSection("DatabaseConnectionSettings"));
            services.Configure <MqttSettings>(this.Configuration.GetSection("MqttSettings"));

            // Load database connection settings
            var databaseConnection =
                this.Configuration.GetSection("DatabaseConnectionSettings").Get <DatabaseConnectionSettings>()
                ?? new DatabaseConnectionSettings();

            // Load MQTT configuration settings
            var mqttSettings = this.Configuration.GetSection("MqttSettings").Get <MqttSettings>();

            // Configure database context
            databaseContext = new MqttContext(databaseConnection);

            // Added the identity stuff and the database connection
            services.AddDbContext <MqttContext>(options => options.UseNpgsql(databaseConnection.ToConnectionString()));

            services.AddIdentity <User, Role>().AddEntityFrameworkStores <MqttContext>().AddDefaultTokenProviders();

            // Add response compression
            services.AddResponseCompression();

            // Add AutoMapper
            services.AddAutoMapper(typeof(UserClaimsProfile), typeof(UserProfile));

            // Add swagger
            // Add swagger document for the API
            services.AddSwaggerDocument(
                config =>
            {
                var version         = Assembly.GetExecutingAssembly().GetName().Version;
                config.DocumentName = $"NetCoreMQTTExampleIdentityConfig {version}";
                config.PostProcess  = document =>
                {
                    document.Info.Version     = $"{version}";
                    document.Info.Title       = "NetCoreMQTTExampleIdentityConfig";
                    document.Info.Description = "NetCoreMQTTExampleIdentityConfig";
                };
            });

            // Read certificate
            var currentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            var certificate = new X509Certificate2(
                Path.Combine(currentPath, "certificate.pfx"),
                "test",
                X509KeyStorageFlags.Exportable);

            // Add MQTT stuff
            services.AddHostedMqttServer(
                builder => builder

                //// .WithDefaultEndpoint().WithDefaultEndpointPort(1883) // For testing purposes only
                .WithDefaultEndpoint().WithDefaultEndpointPort(1883)     // For testing purposes only
                // .WithoutDefaultEndpoint()
                .WithEncryptedEndpoint().WithEncryptedEndpointPort(mqttSettings.Port)
                .WithEncryptionCertificate(certificate.Export(X509ContentType.Pfx))
                .WithEncryptionSslProtocol(SslProtocols.Tls12).WithConnectionValidator(
                    c =>
            {
                var currentUser = databaseContext.Users.FirstOrDefault(u => u.UserName == c.Username);

                if (currentUser == null)
                {
                    c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                    return;
                }

                if (c.Username != currentUser.UserName)
                {
                    c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                    return;
                }

                var hashingResult = Hasher.VerifyHashedPassword(
                    currentUser,
                    currentUser.PasswordHash,
                    c.Password);

                if (hashingResult == PasswordVerificationResult.Failed)
                {
                    c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                    return;
                }

                if (string.IsNullOrWhiteSpace(currentUser.ClientIdPrefix))
                {
                    if (c.ClientId != currentUser.ClientId)
                    {
                        c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                        return;
                    }

                    c.SessionItems.Add(currentUser.ClientId, currentUser);
                }
                else
                {
                    c.SessionItems.Add(currentUser.ClientIdPrefix, currentUser);
                }

                c.ReasonCode = MqttConnectReasonCode.Success;
            }).WithSubscriptionInterceptor(
                    c =>
            {
                var clientIdPrefix = GetClientIdPrefix(c.ClientId);
                User currentUser;
                bool userFound;

                if (clientIdPrefix == null)
                {
                    userFound   = c.SessionItems.TryGetValue(c.ClientId, out var currentUserObject);
                    currentUser = currentUserObject as User;
                }
                else
                {
                    userFound   = c.SessionItems.TryGetValue(clientIdPrefix, out var currentUserObject);
                    currentUser = currentUserObject as User;
                }

                if (!userFound || currentUser == null)
                {
                    c.AcceptSubscription = false;
                    return;
                }

                var topic = c.TopicFilter.Topic;

                // Get blacklist
                var subscriptionBlackList = databaseContext.UserClaims.FirstOrDefault(
                    uc => uc.UserId == currentUser.Id &&
                    uc.ClaimType == ClaimType.SubscriptionBlacklist.ToString());

                var blacklist =
                    JsonConvert.DeserializeObject <List <string> >(subscriptionBlackList?.ClaimValue)
                    ?? new List <string>();

                // Get whitelist
                var subscriptionWhitelist = databaseContext.UserClaims.FirstOrDefault(
                    uc => uc.UserId == currentUser.Id &&
                    uc.ClaimType == ClaimType.SubscriptionWhitelist.ToString());

                var whitelist =
                    JsonConvert.DeserializeObject <List <string> >(subscriptionWhitelist?.ClaimValue)
                    ?? new List <string>();

                // Check matches
                if (blacklist.Contains(topic))
                {
                    c.AcceptSubscription = false;
                    return;
                }

                if (whitelist.Contains(topic))
                {
                    c.AcceptSubscription = true;
                    return;
                }

                foreach (var forbiddenTopic in blacklist)
                {
                    var doesTopicMatch = TopicChecker.Regex(forbiddenTopic, topic);
                    if (!doesTopicMatch)
                    {
                        continue;
                    }

                    c.AcceptSubscription = false;
                    return;
                }

                foreach (var allowedTopic in whitelist)
                {
                    var doesTopicMatch = TopicChecker.Regex(allowedTopic, topic);
                    if (!doesTopicMatch)
                    {
                        continue;
                    }

                    c.AcceptSubscription = true;
                    return;
                }

                c.AcceptSubscription = false;
            }).WithApplicationMessageInterceptor(
                    c =>
            {
                var clientIdPrefix = GetClientIdPrefix(c.ClientId);
                User currentUser;
                bool userFound;

                if (clientIdPrefix == null)
                {
                    userFound   = c.SessionItems.TryGetValue(c.ClientId, out var currentUserObject);
                    currentUser = currentUserObject as User;
                }
                else
                {
                    userFound   = c.SessionItems.TryGetValue(clientIdPrefix, out var currentUserObject);
                    currentUser = currentUserObject as User;
                }

                if (!userFound || currentUser == null)
                {
                    c.AcceptPublish = false;
                    return;
                }

                var topic = c.ApplicationMessage.Topic;

                // Get blacklist
                var subscriptionBlackList = databaseContext.UserClaims.FirstOrDefault(
                    uc => uc.UserId == currentUser.Id &&
                    uc.ClaimType == ClaimType.PublishBlacklist.ToString());

                var blacklist =
                    JsonConvert.DeserializeObject <List <string> >(subscriptionBlackList?.ClaimValue)
                    ?? new List <string>();

                // Get whitelist
                var subscriptionWhitelist = databaseContext.UserClaims.FirstOrDefault(
                    uc => uc.UserId == currentUser.Id &&
                    uc.ClaimType == ClaimType.PublishWhitelist.ToString());

                var whitelist =
                    JsonConvert.DeserializeObject <List <string> >(subscriptionWhitelist?.ClaimValue)
                    ?? new List <string>();

                // Check matches
                if (blacklist.Contains(topic))
                {
                    c.AcceptPublish = false;
                    return;
                }

                if (whitelist.Contains(topic))
                {
                    c.AcceptPublish = true;
                    return;
                }

                foreach (var forbiddenTopic in blacklist)
                {
                    var doesTopicMatch = TopicChecker.Regex(forbiddenTopic, topic);
                    if (!doesTopicMatch)
                    {
                        continue;
                    }

                    c.AcceptPublish = false;
                    return;
                }

                foreach (var allowedTopic in whitelist)
                {
                    var doesTopicMatch = TopicChecker.Regex(allowedTopic, topic);
                    if (!doesTopicMatch)
                    {
                        continue;
                    }

                    c.AcceptPublish = true;
                    return;
                }

                c.AcceptPublish = false;
            }));

            services.AddMqttConnectionHandler();

            // Add the MVC stuff
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }