public void CheckSingleValuePlusMatchWithPlus() { var result = TopicChecker.Regex("a/+", "a/+"); Assert.IsTrue(result); }
public void CheckMultipleValuePlusMatchNoOperators() { var result = TopicChecker.Regex("a/+/+/a", "a/b/b/a"); Assert.IsTrue(result); }
public void CheckSingleValuePlusDontMatchWithCross() { var result = TopicChecker.Regex("a/+", "a/#"); Assert.IsFalse(result); }
public void CheckMultipleValueCrossMatchMixed() { var result = TopicChecker.Regex("a/#", "a/+/a/#"); Assert.IsTrue(result); }
public void CheckMultipleValueCrossDontMatchMixed() { var result = TopicChecker.Regex("a/#", "a/+/+/#/a"); Assert.IsFalse(result); }
/// <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); }
/// <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); }
/// <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); }
/// <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; }
/// <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); }
/// <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); }