private static bool CheckForFlooding(IInvocation invocation, Boolean blockWhenFlooding) { bool blockRequest = false; string requestIP = Helper.MapRequestIP(); string obfuscatedIP = Helper.HashAndTruncate(requestIP); if (Holder.connectionStore.ContainsKey(requestIP) && Holder.connectionStore[requestIP].LastDataSend != null) { // We have seen this IP before PersistentMapAPI.Settings settings = Helper.LoadSettings(); UserInfo info = Holder.connectionStore[requestIP]; string lastDateSendISO = info.LastDataSend.ToString("u"); DateTime now = DateTime.UtcNow; DateTime blockedUntil = info.LastDataSend.AddMinutes(settings.minMinutesBetweenPost); TimeSpan delta = now.Subtract(info.LastDataSend); string deltaS = $"{(int)delta.TotalMinutes}:{delta.Seconds:00}"; if (now >= blockedUntil) { // The user hasn't sent a message within the time limit, so just note it when tracing is enabled logger.Trace($"IP:{(settings.Debug ? requestIP : obfuscatedIP)} last send a request {deltaS} ago."); } else { // User is flooding. We should send back a 429 (Too Many Requests) but WCF isn't there yet. Send back a 403 for now. // TODO: Verify this breaks the client as expected - with an error (cannot upload) // TOOD: Add a better error message on the client for this case string floodingMsg = $"IP: Flooding from IP:({(settings.Debug ? requestIP : obfuscatedIP)}) - last successful request was ({lastDateSendISO}) which was {deltaS} ago."; if (blockWhenFlooding) { logger.Info(floodingMsg); blockRequest = true; } else { // The attribute is marked as log only, so log a warning logger.Warn(floodingMsg); } } } else { // We haven't seen this IP before, so go ahead and let it through logger.Trace($"IP: Unrecognized IP, so allowing request."); } return(blockRequest); }
// Find all of the players that have been active within N minutes. Return their UserInfos keyed by system name private static Dictionary <string, List <UserInfo> > GetSystemsWithActivePlayers(int numSystems) { PersistentMapAPI.Settings settings = Helper.LoadSettings(); DateTime now = DateTime.UtcNow; Dictionary <string, List <UserInfo> > playersBySystem = Holder.connectionStore .AsParallel() .Where(x => x.Value.LastDataSend.AddMinutes(settings.MinutesForActive) > now) .AsSequential() .GroupBy(p => p.Value.lastSystemFoughtAt) .ToDictionary(p => p.Key, p => p.Select(g => g.Value).ToList()); logger.Trace($"Mapped players to { playersBySystem.Keys.Count} systems."); return(playersBySystem); }
public void Intercept(IInvocation invocation) { bool preventMethodInvocation = false; foreach (System.Attribute attribute in invocation.GetConcreteMethod().GetCustomAttributes(false)) { if (attribute.GetType() == typeof(AdminKeyRequiredAttribute)) { // Method is decorated var settings = Helper.LoadSettings(); var properties = OperationContext.Current.IncomingMessageProperties; var property = properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; WebHeaderCollection headers = property.Headers; var headerValue = headers != null && headers.Get(AdminKeyRequiredAttribute.HeaderName) != null? headers.Get(AdminKeyRequiredAttribute.HeaderName) : ""; if (!headerValue.Equals(settings.AdminKey)) { // Header value doesn't match, block access. Otherwise, let it through preventMethodInvocation = true; } } } if (preventMethodInvocation) { // Prevent the method from executing WebOperationContext context = WebOperationContext.Current; context.OutgoingResponse.StatusCode = HttpStatusCode.Forbidden; context.OutgoingResponse.StatusDescription = $"Access denied."; invocation.ReturnValue = null; IncomingWebRequestContext requestContext = WebOperationContext.Current.IncomingRequest; string serviceMethod = requestContext.UriTemplateMatch != null?requestContext.UriTemplateMatch.Data.ToString() : "UNMAPPED"; string requestIP = Helper.MapRequestIP(); string obfuscatedIP = Helper.HashAndTruncate(requestIP); PersistentMapAPI.Settings settings = Helper.LoadSettings(); logger.Warn($"Prevented unauthorized access from ({( settings.Debug ? requestIP : obfuscatedIP )}) to method {serviceMethod}"); } else { // Allow the method to execute normally invocation.Proceed(); } }