Last active
June 9, 2016 14:40
-
-
Save cameronsjo/d3eff20e7abc1cab3fe64e58aaee0563 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections; | |
| using System.Configuration; | |
| using System.Linq; | |
| using System.Text; | |
| using System.Web; | |
| using System.Web.Http; | |
| using System.Web.Http.Controllers; | |
| namespace Filters | |
| { | |
| /// <summary> | |
| /// Filter by IP address (ASP.NET Web API version) | |
| /// Originally from http://stackoverflow.com/a/4605086/91551 | |
| /// http://www.codeproject.com/Articles/2553/IP-list-Check-an-IP-number-against-a-list-in-C | |
| /// </summary> | |
| public class FilterIpAttribute : AuthorizeAttribute | |
| { | |
| /// <summary> | |
| /// Comma seperated string of allowable IPs. Example "10.2.5.41,192.168.0.22" | |
| /// </summary> | |
| /// <value></value> | |
| public string AllowedSingleIPs { get; set; } | |
| public bool AllowLocal { get; set; } | |
| /// <summary> | |
| /// Comma seperated string of allowable IPs with masks. Example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0" | |
| /// </summary> | |
| /// <value>The masked I ps.</value> | |
| public string AllowedMaskedIPs { get; set; } | |
| /// <summary> | |
| /// List of allowed IPs | |
| /// </summary> | |
| private readonly IpList _allowedIpListToCheck = new IpList(); | |
| /// <summary> | |
| /// Comma seperated string of denied IPs. Example "10.2.5.41,192.168.0.22" | |
| /// </summary> | |
| /// <value></value> | |
| public string DeniedSingleIPs { get; set; } | |
| /// <summary> | |
| /// Comma seperated string of denied IPs with masks. Example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0" | |
| /// </summary> | |
| /// <value>The masked I ps.</value> | |
| public string DeniedMaskedIPs { get; set; } | |
| /// <summary> | |
| /// List of denied IPs | |
| /// </summary> | |
| private readonly IpList _deniedIpListToCheck = new IpList(); | |
| private bool AuthorizeCore(HttpContextBase httpContext) | |
| { | |
| if (httpContext == null) | |
| throw new ArgumentNullException("httpContext"); | |
| if (Environment.CurrentServer != CurrentEnvironment.Development) | |
| { | |
| return true; | |
| } | |
| var userIpAddress = GetRealIp(httpContext.Request); | |
| // Based on my limited knowledge, this is just another check to see if it's running locally. | |
| if (userIpAddress == "::1") | |
| return true; | |
| try | |
| { | |
| // Check that the IP is allowed to access | |
| var ipAllowed = CheckAllowedIPs(userIpAddress); | |
| // Check that the IP is not denied to access | |
| var ipDenied = CheckDeniedIPs(userIpAddress); | |
| // Only allowed if allowed and not denied | |
| var finallyAllowed = ipAllowed && !ipDenied; | |
| return finallyAllowed; | |
| } | |
| catch (Exception e) | |
| { | |
| // Log the exception, probably something wrong with the configuration | |
| } | |
| return true; // if there was an exception, then we return true | |
| } | |
| /// <summary> | |
| /// Checks the allowed IPs. | |
| /// </summary> | |
| /// <param name="userIpAddress">The user ip address.</param> | |
| /// <returns></returns> | |
| private bool CheckAllowedIPs(string userIpAddress) | |
| { | |
| if (AllowLocal) | |
| { | |
| // These addresses were pulled from https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4 | |
| SplitAndAddMaskedIPs("10.0.0.0/8," + | |
| "100.64.0.0/10," + | |
| "127.0.0.0/8," + | |
| "172.16.0.0/12," + | |
| "192.0.0.0/24," + | |
| "192.168.0.0/16," + | |
| "198.18.0.0/15", _allowedIpListToCheck); | |
| } | |
| // Populate the IPList with the Single IPs | |
| if (!string.IsNullOrEmpty(AllowedSingleIPs)) | |
| { | |
| SplitAndAddSingleIPs(AllowedSingleIPs, _allowedIpListToCheck); | |
| } | |
| // Populate the IPList with the Masked IPs | |
| if (!string.IsNullOrEmpty(AllowedMaskedIPs)) | |
| { | |
| SplitAndAddMaskedIPs(AllowedMaskedIPs, _allowedIpListToCheck); | |
| } | |
| return _allowedIpListToCheck.CheckNumber(userIpAddress); | |
| } | |
| /// <summary> | |
| /// Checks the denied IPs. | |
| /// </summary> | |
| /// <param name="userIpAddress">The user ip address.</param> | |
| /// <returns></returns> | |
| private bool CheckDeniedIPs(string userIpAddress) | |
| { | |
| // Populate the IPList with the Single IPs | |
| if (!string.IsNullOrEmpty(DeniedSingleIPs)) | |
| { | |
| SplitAndAddSingleIPs(DeniedSingleIPs, _deniedIpListToCheck); | |
| } | |
| // Populate the IPList with the Masked IPs | |
| if (!string.IsNullOrEmpty(DeniedMaskedIPs)) | |
| { | |
| SplitAndAddMaskedIPs(DeniedMaskedIPs, _deniedIpListToCheck); | |
| } | |
| return _deniedIpListToCheck.CheckNumber(userIpAddress); | |
| } | |
| /// <summary> | |
| /// Splits the incoming ip string of the format "IP,IP" example "10.2.0.0,10.3.0.0" and adds the result to the IPList | |
| /// </summary> | |
| /// <param name="ips">The ips.</param> | |
| /// <param name="list">The list.</param> | |
| private void SplitAndAddSingleIPs(string ips, IpList list) | |
| { | |
| var splitSingleIPs = ips.Split(','); | |
| foreach (var ip in splitSingleIPs) | |
| list.Add(ip); | |
| } | |
| /// <summary> | |
| /// Splits the incoming ip string of the format "IP;MASK,IP;MASK" example "10.2.0.0;255.255.0.0,10.3.0.0;255.255.0.0" and adds the result to the IPList | |
| /// </summary> | |
| /// <param name="ips">The ips.</param> | |
| /// <param name="list">The list.</param> | |
| private void SplitAndAddMaskedIPs(string ips, IpList list) | |
| { | |
| var splitMaskedIPs = ips.Split(','); | |
| foreach (var maskedIp in splitMaskedIPs) | |
| { | |
| if (maskedIp.Contains("/")) | |
| { | |
| var ipAndBlock = maskedIp.Split('/'); | |
| list.Add(ipAndBlock[0], int.Parse(ipAndBlock[1])); | |
| } | |
| else | |
| { | |
| var ipAndMask = maskedIp.Split(';'); | |
| list.Add(ipAndMask[0], ipAndMask[1]); // IP;MASK | |
| } | |
| } | |
| } | |
| public override void OnAuthorization(HttpActionContext actionContext) | |
| { | |
| if (AuthorizeCore((HttpContextBase)actionContext.Request.Properties["MS_HttpContext"])) | |
| return; | |
| base.HandleUnauthorizedRequest(actionContext); | |
| } | |
| public static string GetRealIp(HttpRequestBase request) | |
| { | |
| // This is used in the case that a load balancer is hit. | |
| var ip = request["HTTP_X_FORWARDED_FOR"]; | |
| if (!string.IsNullOrWhiteSpace(ip)) | |
| { | |
| ip = ip.Split(',').Last().Trim(); | |
| } | |
| if (string.IsNullOrWhiteSpace(ip)) | |
| { | |
| ip = request.UserHostAddress; | |
| } | |
| return ip; | |
| } | |
| /// <summary> | |
| /// Internal class for storing a range of IP numbers with the same IP mask | |
| /// </summary> | |
| internal class IpArrayList | |
| { | |
| private bool _isSorted = false; | |
| private readonly ArrayList _ipNumList = new ArrayList(); | |
| private readonly uint _ipmask; | |
| /// <summary> | |
| /// Constructor that sets the mask for the list | |
| /// </summary> | |
| public IpArrayList(uint mask) | |
| { | |
| _ipmask = mask; | |
| } | |
| /// <summary> | |
| /// Add a new IP numer (range) to the list | |
| /// </summary> | |
| public void Add(uint ipNum) | |
| { | |
| _isSorted = false; | |
| _ipNumList.Add(ipNum & _ipmask); | |
| } | |
| /// <summary> | |
| /// Checks if an IP number is within the ranges included by the list | |
| /// </summary> | |
| public bool Check(uint ipNum) | |
| { | |
| var found = false; | |
| if (_ipNumList.Count > 0) | |
| { | |
| if (!_isSorted) | |
| { | |
| _ipNumList.Sort(); | |
| _isSorted = true; | |
| } | |
| ipNum = ipNum & _ipmask; | |
| if (_ipNumList.BinarySearch(ipNum) >= 0) found = true; | |
| } | |
| return found; | |
| } | |
| /// <summary> | |
| /// Clears the list | |
| /// </summary> | |
| public void Clear() | |
| { | |
| _ipNumList.Clear(); | |
| _isSorted = false; | |
| } | |
| /// <summary> | |
| /// The ToString is overriden to generate a list of the IP numbers | |
| /// </summary> | |
| public override string ToString() | |
| { | |
| var buf = new StringBuilder(); | |
| foreach (uint ipnum in _ipNumList) | |
| { | |
| if (buf.Length > 0) buf.Append("\r\n"); | |
| buf.Append(((int)ipnum & 0xFF000000) >> 24).Append('.'); | |
| buf.Append(((int)ipnum & 0x00FF0000) >> 16).Append('.'); | |
| buf.Append(((int)ipnum & 0x0000FF00) >> 8).Append('.'); | |
| buf.Append(((int)ipnum & 0x000000FF)); | |
| } | |
| return buf.ToString(); | |
| } | |
| /// <summary> | |
| /// The IP mask for this list of IP numbers | |
| /// </summary> | |
| public uint Mask | |
| { | |
| get | |
| { | |
| return _ipmask; | |
| } | |
| } | |
| } | |
| internal class IpList | |
| { | |
| private readonly ArrayList _ipRangeList = new ArrayList(); | |
| private readonly SortedList _maskList = new SortedList(); | |
| private readonly ArrayList _usedList = new ArrayList(); | |
| public IpList() | |
| { | |
| // Initialize IP mask list and create IPArrayList into the ipRangeList | |
| uint mask = 0x00000000; | |
| for (var level = 1; level < 33; level++) | |
| { | |
| mask = (mask >> 1) | 0x80000000; | |
| _maskList.Add(mask, level); | |
| _ipRangeList.Add(new IpArrayList(mask)); | |
| } | |
| } | |
| // Parse a String IP address to a 32 bit unsigned integer | |
| // We can't use System.Net.IPAddress as it will not parse | |
| // our masks correctly eg. 255.255.0.0 is pased as 65535 ! | |
| private uint ParseIp(string ipNumber) | |
| { | |
| uint res = 0; | |
| var elements = ipNumber.Split(new Char[] { '.' }); | |
| if (elements.Length != 4) | |
| return res; | |
| res = (uint)Convert.ToInt32(elements[0]) << 24; | |
| res += (uint)Convert.ToInt32(elements[1]) << 16; | |
| res += (uint)Convert.ToInt32(elements[2]) << 8; | |
| res += (uint)Convert.ToInt32(elements[3]); | |
| return res; | |
| } | |
| /// <summary> | |
| /// Add a single IP number to the list as a string, ex. 10.1.1.1 | |
| /// </summary> | |
| public void Add(string ipNumber) | |
| { | |
| this.Add(ParseIp(ipNumber)); | |
| } | |
| /// <summary> | |
| /// Add a single IP number to the list as a unsigned integer, ex. 0x0A010101 | |
| /// </summary> | |
| public void Add(uint ip) | |
| { | |
| ((IpArrayList)_ipRangeList[31]).Add(ip); | |
| if (!_usedList.Contains((int)31)) | |
| { | |
| _usedList.Add((int)31); | |
| _usedList.Sort(); | |
| } | |
| } | |
| /// <summary> | |
| /// Adds IP numbers using a mask for range where the mask specifies the number of | |
| /// fixed bits, ex. 172.16.0.0 255.255.0.0 will add 172.16.0.0 - 172.16.255.255 | |
| /// </summary> | |
| public void Add(string ipNumber, string mask) | |
| { | |
| this.Add(ParseIp(ipNumber), ParseIp(mask)); | |
| } | |
| /// <summary> | |
| /// Adds IP numbers using a mask for range where the mask specifies the number of | |
| /// fixed bits, ex. 0xAC1000 0xFFFF0000 will add 172.16.0.0 - 172.16.255.255 | |
| /// </summary> | |
| public void Add(uint ip, uint umask) | |
| { | |
| var level = _maskList[umask]; | |
| if (level != null) | |
| { | |
| ip = ip & umask; | |
| ((IpArrayList)_ipRangeList[(int)level - 1]).Add(ip); | |
| if (!_usedList.Contains((int)level - 1)) | |
| { | |
| _usedList.Add((int)level - 1); | |
| _usedList.Sort(); | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Adds IP numbers using a mask for range where the mask specifies the number of | |
| /// fixed bits, ex. 192.168.1.0/24 which will add 192.168.1.0 - 192.168.1.255 | |
| /// </summary> | |
| public void Add(string ipNumber, int maskLevel) | |
| { | |
| this.Add(ParseIp(ipNumber), (uint)_maskList.GetKey(_maskList.IndexOfValue(maskLevel))); | |
| } | |
| /// <summary> | |
| /// Adds IP numbers using a from and to IP number. The method checks the range and | |
| /// splits it into normal ip/mask blocks. | |
| /// </summary> | |
| public void AddRange(string fromIp, string toIp) | |
| { | |
| this.AddRange(ParseIp(fromIp), ParseIp(toIp)); | |
| } | |
| /// <summary> | |
| /// Adds IP numbers using a from and to IP number. The method checks the range and | |
| /// splits it into normal ip/mask blocks. | |
| /// </summary> | |
| public void AddRange(uint fromIp, uint toIp) | |
| { | |
| // If the order is not asending, switch the IP numbers. | |
| if (fromIp > toIp) | |
| { | |
| var tempIp = fromIp; | |
| fromIp = toIp; | |
| toIp = tempIp; | |
| } | |
| if (fromIp == toIp) | |
| { | |
| this.Add(fromIp); | |
| } | |
| else | |
| { | |
| var diff = toIp - fromIp; | |
| var diffLevel = 1; | |
| var range = 0x80000000; | |
| if (diff < 256) | |
| { | |
| diffLevel = 24; | |
| range = 0x00000100; | |
| } | |
| while (range > diff) | |
| { | |
| range = range >> 1; | |
| diffLevel++; | |
| } | |
| var mask = (uint)_maskList.GetKey(_maskList.IndexOfValue(diffLevel)); | |
| var minIp = fromIp & mask; | |
| if (minIp < fromIp) minIp += range; | |
| if (minIp > fromIp) | |
| { | |
| this.AddRange(fromIp, minIp - 1); | |
| fromIp = minIp; | |
| } | |
| if (fromIp == toIp) | |
| { | |
| this.Add(fromIp); | |
| } | |
| else | |
| { | |
| if ((minIp + (range - 1)) <= toIp) | |
| { | |
| this.Add(minIp, mask); | |
| fromIp = minIp + range; | |
| } | |
| if (fromIp == toIp) | |
| { | |
| this.Add(toIp); | |
| } | |
| else | |
| { | |
| if (fromIp < toIp) this.AddRange(fromIp, toIp); | |
| } | |
| } | |
| } | |
| } | |
| /// <summary> | |
| /// Checks if an IP number is contained in the lists, ex. 10.0.0.1 | |
| /// </summary> | |
| public bool CheckNumber(string ipNumber) | |
| { | |
| return this.CheckNumber(ParseIp(ipNumber)); ; | |
| } | |
| /// <summary> | |
| /// Checks if an IP number is contained in the lists, ex. 0x0A000001 | |
| /// </summary> | |
| public bool CheckNumber(uint ip) | |
| { | |
| var found = false; | |
| var i = 0; | |
| while (!found && i < _usedList.Count) | |
| { | |
| found = ((IpArrayList)_ipRangeList[(int)_usedList[i]]).Check(ip); | |
| i++; | |
| } | |
| return found; | |
| } | |
| /// <summary> | |
| /// Clears all lists of IP numbers | |
| /// </summary> | |
| public void Clear() | |
| { | |
| foreach (int i in _usedList) | |
| { | |
| ((IpArrayList)_ipRangeList[i]).Clear(); | |
| } | |
| _usedList.Clear(); | |
| } | |
| /// <summary> | |
| /// Generates a list of all IP ranges in printable format | |
| /// </summary> | |
| public override string ToString() | |
| { | |
| var buffer = new StringBuilder(); | |
| foreach (int i in _usedList) | |
| { | |
| buffer.Append("\r\nRange with mask of ").Append(i + 1).Append("\r\n"); | |
| buffer.Append(((IpArrayList)_ipRangeList[i]).ToString()); | |
| } | |
| return buffer.ToString(); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment