HttpListenerContext.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. using System;
  2. using System.Net;
  3. using System.Security.Principal;
  4. using MediaBrowser.Model.Cryptography;
  5. using MediaBrowser.Model.IO;
  6. using MediaBrowser.Model.Logging;
  7. using MediaBrowser.Model.Text;
  8. using SocketHttpListener.Net.WebSockets;
  9. using SocketHttpListener.Primitives;
  10. namespace SocketHttpListener.Net
  11. {
  12. public sealed class HttpListenerContext
  13. {
  14. HttpListenerRequest request;
  15. HttpListenerResponse response;
  16. IPrincipal user;
  17. HttpConnection cnc;
  18. string error;
  19. int err_status = 400;
  20. private readonly ICryptoProvider _cryptoProvider;
  21. private readonly IMemoryStreamFactory _memoryStreamFactory;
  22. private readonly ITextEncoding _textEncoding;
  23. internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem)
  24. {
  25. this.cnc = cnc;
  26. _cryptoProvider = cryptoProvider;
  27. _memoryStreamFactory = memoryStreamFactory;
  28. _textEncoding = textEncoding;
  29. request = new HttpListenerRequest(this, _textEncoding);
  30. response = new HttpListenerResponse(this, logger, _textEncoding, fileSystem);
  31. }
  32. internal int ErrorStatus
  33. {
  34. get { return err_status; }
  35. set { err_status = value; }
  36. }
  37. internal string ErrorMessage
  38. {
  39. get { return error; }
  40. set { error = value; }
  41. }
  42. internal bool HaveError
  43. {
  44. get { return (error != null); }
  45. }
  46. internal HttpConnection Connection
  47. {
  48. get { return cnc; }
  49. }
  50. public HttpListenerRequest Request
  51. {
  52. get { return request; }
  53. }
  54. public HttpListenerResponse Response
  55. {
  56. get { return response; }
  57. }
  58. public IPrincipal User
  59. {
  60. get { return user; }
  61. }
  62. internal void ParseAuthentication(AuthenticationSchemes expectedSchemes)
  63. {
  64. if (expectedSchemes == AuthenticationSchemes.Anonymous)
  65. return;
  66. // TODO: Handle NTLM/Digest modes
  67. string header = request.Headers["Authorization"];
  68. if (header == null || header.Length < 2)
  69. return;
  70. string[] authenticationData = header.Split(new char[] { ' ' }, 2);
  71. if (string.Equals(authenticationData[0], "basic", StringComparison.OrdinalIgnoreCase))
  72. {
  73. user = ParseBasicAuthentication(authenticationData[1]);
  74. }
  75. // TODO: throw if malformed -> 400 bad request
  76. }
  77. internal IPrincipal ParseBasicAuthentication(string authData)
  78. {
  79. try
  80. {
  81. // Basic AUTH Data is a formatted Base64 String
  82. //string domain = null;
  83. string user = null;
  84. string password = null;
  85. int pos = -1;
  86. var authDataBytes = Convert.FromBase64String(authData);
  87. string authString = _textEncoding.GetDefaultEncoding().GetString(authDataBytes, 0, authDataBytes.Length);
  88. // The format is DOMAIN\username:password
  89. // Domain is optional
  90. pos = authString.IndexOf(':');
  91. // parse the password off the end
  92. password = authString.Substring(pos + 1);
  93. // discard the password
  94. authString = authString.Substring(0, pos);
  95. // check if there is a domain
  96. pos = authString.IndexOf('\\');
  97. if (pos > 0)
  98. {
  99. //domain = authString.Substring (0, pos);
  100. user = authString.Substring(pos);
  101. }
  102. else
  103. {
  104. user = authString;
  105. }
  106. HttpListenerBasicIdentity identity = new HttpListenerBasicIdentity(user, password);
  107. // TODO: What are the roles MS sets
  108. return new GenericPrincipal(identity, new string[0]);
  109. }
  110. catch (Exception)
  111. {
  112. // Invalid auth data is swallowed silently
  113. return null;
  114. }
  115. }
  116. public HttpListenerWebSocketContext AcceptWebSocket(string protocol)
  117. {
  118. if (protocol != null)
  119. {
  120. if (protocol.Length == 0)
  121. throw new ArgumentException("An empty string.", "protocol");
  122. if (!protocol.IsToken())
  123. throw new ArgumentException("Contains an invalid character.", "protocol");
  124. }
  125. return new HttpListenerWebSocketContext(this, protocol, _cryptoProvider, _memoryStreamFactory);
  126. }
  127. }
  128. public class GenericPrincipal : IPrincipal
  129. {
  130. private IIdentity m_identity;
  131. private string[] m_roles;
  132. public GenericPrincipal(IIdentity identity, string[] roles)
  133. {
  134. if (identity == null)
  135. throw new ArgumentNullException("identity");
  136. m_identity = identity;
  137. if (roles != null)
  138. {
  139. m_roles = new string[roles.Length];
  140. for (int i = 0; i < roles.Length; ++i)
  141. {
  142. m_roles[i] = roles[i];
  143. }
  144. }
  145. else
  146. {
  147. m_roles = null;
  148. }
  149. }
  150. public virtual IIdentity Identity
  151. {
  152. get
  153. {
  154. return m_identity;
  155. }
  156. }
  157. public virtual bool IsInRole(string role)
  158. {
  159. if (role == null || m_roles == null)
  160. return false;
  161. for (int i = 0; i < m_roles.Length; ++i)
  162. {
  163. if (m_roles[i] != null && String.Compare(m_roles[i], role, StringComparison.OrdinalIgnoreCase) == 0)
  164. return true;
  165. }
  166. return false;
  167. }
  168. }
  169. }