/IdentityModel/Thinktecture.IdentityModel/Http/Hawk/Core/NormalizedRequest.cs

https://github.com/hanimourra/Thinktecture.IdentityModel.45 · C# · 135 lines · 89 code · 20 blank · 26 comment · 11 complexity · 58efc2a60db8eb614c3dbf1170715dd2 MD5 · raw file

  1. using System;
  2. using System.Linq;
  3. using System.Net.Http;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using Thinktecture.IdentityModel.Http.Hawk.Core.Extensions;
  7. using Thinktecture.IdentityModel.Http.Hawk.Core.Helpers;
  8. using Thinktecture.IdentityModel.Http.Hawk.Core.MessageContracts;
  9. namespace Thinktecture.IdentityModel.Http.Hawk.Core
  10. {
  11. /// <summary>
  12. /// Represents the normalized request, in the following format.
  13. /// hawk.1.header\n
  14. /// timestamp\n
  15. /// nonce\n
  16. /// HTTP method\n
  17. /// uri path and query string\n
  18. /// host name\n
  19. /// port\n
  20. /// payload hash\n
  21. /// application specific data\n
  22. /// </summary>
  23. internal class NormalizedRequest
  24. {
  25. private const string REQUEST_PREAMBLE = HawkConstants.Scheme + "." + HawkConstants.Version + ".header"; // hawk.1.header
  26. private const string BEWIT_PREAMBLE = HawkConstants.Scheme + "." + HawkConstants.Version + ".bewit"; // hawk.1.bewit
  27. private const string RESPONSE_PREAMBLE = HawkConstants.Scheme + "." + HawkConstants.Version + ".response"; // hawk.1.response
  28. private const string HTTP_PORT = "80";
  29. private const string HTTPS_PORT = "443";
  30. private const string MATCH_PATTERN_HOSTNAME_OR_IPV4 = @"^(?:(?:\r\n)?\s)*([^:]+)(?::(\d+))?(?:(?:\r\n)?\s)*$";
  31. private const string MATCH_PATTERN_IPV6 = @"^(?:(?:\r\n)?\s)*(\[[^\]]+\])(?::(\d+))?(?:(?:\r\n)?\s)*$";
  32. private const string XFF_HEADER_NAME = "X-Forwarded-For";
  33. private readonly ArtifactsContainer artifacts = null;
  34. private readonly string method = null;
  35. private readonly string path = null;
  36. private readonly string hostName = null;
  37. private readonly string port = null;
  38. internal NormalizedRequest(IRequestMessage request, ArtifactsContainer artifacts)
  39. {
  40. this.artifacts = artifacts;
  41. // Determine host and port - take the host name from X-Forwarded-For header, if present, or from
  42. // the Host header, if present, or from the HttpRequestMessage object. For bewit, it is always from URI.
  43. string firstPreference = IsBewit? null : request.ForwardedFor;
  44. string secondPreference = IsBewit ? null : request.Host;
  45. this.hostName = this.GetHostName(firstPreference, out this.port) ??
  46. this.GetHostName(secondPreference, out this.port) ??
  47. request.Uri.Host;
  48. if (String.IsNullOrWhiteSpace(this.port))
  49. this.port = request.Uri.Port.ToString();
  50. this.method = request.Method.Method.ToUpper();
  51. this.path = request.Uri.PathAndQuery;
  52. }
  53. /// <summary>
  54. /// Set to true, if this instance is for a bewit.
  55. /// </summary>
  56. internal bool IsBewit { get; set; }
  57. /// <summary>
  58. /// Set to true, if this instance is for server authorization response.
  59. /// </summary>
  60. internal bool IsServerAuthorization { get; set; }
  61. /// <summary>
  62. /// Returns the normalized request string.
  63. /// </summary>
  64. public override string ToString()
  65. {
  66. StringBuilder result = new StringBuilder();
  67. result
  68. .AppendNewLine(this.GetPreamble())
  69. .AppendNewLine(artifacts.Timestamp.ToString())
  70. .AppendNewLine(artifacts.Nonce)
  71. .AppendNewLine(this.method)
  72. .AppendNewLine(this.path)
  73. .AppendNewLine(this.hostName)
  74. .AppendNewLine(this.port)
  75. .AppendNewLine(artifacts.PayloadHash == null ? null : artifacts.PayloadHash.ToBase64String())
  76. .AppendNewLine(artifacts.ApplicationSpecificData);
  77. return result.ToString();
  78. }
  79. /// <summary>
  80. /// Returns the normalized request bytes.
  81. /// </summary>
  82. internal byte[] ToBytes()
  83. {
  84. return this.ToString().ToBytesFromUtf8();
  85. }
  86. private string GetHostName(string hostHeader, out string port)
  87. {
  88. if (!String.IsNullOrWhiteSpace(hostHeader))
  89. {
  90. string pattern = hostHeader[0] == '[' ? MATCH_PATTERN_IPV6 : MATCH_PATTERN_HOSTNAME_OR_IPV4;
  91. var match = Regex.Match(hostHeader, pattern);
  92. if (match.Success && match.Groups.Count == 3)
  93. {
  94. string hostName = match.Groups[1].Value;
  95. if (!String.IsNullOrWhiteSpace(hostName))
  96. {
  97. port = match.Groups[2].Value;
  98. return hostName;
  99. }
  100. }
  101. }
  102. port = null;
  103. return null;
  104. }
  105. private string GetPreamble()
  106. {
  107. string preamble = REQUEST_PREAMBLE;
  108. if (this.IsBewit)
  109. preamble = BEWIT_PREAMBLE;
  110. else if (this.IsServerAuthorization)
  111. preamble = RESPONSE_PREAMBLE;
  112. return preamble;
  113. }
  114. }
  115. }