PageRenderTime 66ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/Raven.Database/Server/Security/MixedModeRequestAuthorizer.cs

http://github.com/ayende/ravendb
C# | 258 lines | 223 code | 34 blank | 1 comment | 41 complexity | b83544676f6a776d4cb6ecfb9920d660 MD5 | raw file
Possible License(s): GPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Apache-2.0, BSD-3-Clause, CC-BY-SA-3.0
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Net;
  6. using System.Net.Http;
  7. using System.Security.Principal;
  8. using Raven.Abstractions.Data;
  9. using Raven.Database.Extensions;
  10. using Raven.Database.Server.Controllers;
  11. using Raven.Database.Server.Security.OAuth;
  12. using Raven.Database.Server.Security.Windows;
  13. using System.Linq;
  14. namespace Raven.Database.Server.Security
  15. {
  16. public class MixedModeRequestAuthorizer : AbstractRequestAuthorizer
  17. {
  18. private readonly WindowsRequestAuthorizer windowsRequestAuthorizer = new WindowsRequestAuthorizer();
  19. private readonly OAuthRequestAuthorizer oAuthRequestAuthorizer = new OAuthRequestAuthorizer();
  20. private readonly ConcurrentDictionary<string, OneTimeToken> singleUseAuthTokens = new ConcurrentDictionary<string, OneTimeToken>();
  21. private class OneTimeToken
  22. {
  23. readonly Stopwatch age = Stopwatch.StartNew();
  24. private IPrincipal user;
  25. public string ResourceName { get; set; }
  26. public IPrincipal User
  27. {
  28. get
  29. {
  30. return user;
  31. }
  32. set
  33. {
  34. if (value == null)
  35. {
  36. user = null;
  37. return;
  38. }
  39. user = new OneTimeTokenPrincipal
  40. {
  41. Name = value.Identity.Name,
  42. IsAdministratorInAnonymouseMode = value.IsAdministrator(AnonymousUserAccessMode.None)
  43. };
  44. }
  45. }
  46. public TimeSpan Age
  47. {
  48. get { return age.Elapsed; }
  49. }
  50. }
  51. protected override void Initialize()
  52. {
  53. windowsRequestAuthorizer.Initialize(database, settings, tenantId, server);
  54. oAuthRequestAuthorizer.Initialize(database, settings, tenantId, server);
  55. base.Initialize();
  56. }
  57. public bool TryAuthorize(RavenBaseApiController controller, out HttpResponseMessage msg)
  58. {
  59. var requestUrl = controller.GetRequestUrl();
  60. if (NeverSecret.IsNeverSecretUrl(requestUrl))
  61. {
  62. msg = controller.GetEmptyMessage();
  63. return true;
  64. }
  65. //CORS pre-flight (ignore creds if using cors).
  66. if (Settings.AccessControlAllowOrigin.Count > 0 && controller.InnerRequest.Method.Method == "OPTIONS")
  67. {
  68. msg = controller.GetEmptyMessage();
  69. return true;
  70. }
  71. var oneTimeToken = controller.GetHeader("Single-Use-Auth-Token");
  72. if (string.IsNullOrEmpty(oneTimeToken))
  73. {
  74. oneTimeToken = controller.GetQueryStringValue("singleUseAuthToken");
  75. }
  76. if (string.IsNullOrEmpty(oneTimeToken) == false)
  77. {
  78. return TryAuthorizeSingleUseAuthToken(controller, oneTimeToken, out msg);
  79. }
  80. var authHeader = controller.GetHeader("Authorization");
  81. var hasApiKey = "True".Equals(controller.GetHeader("Has-Api-Key"), StringComparison.CurrentCultureIgnoreCase);
  82. var hasOAuthTokenInCookie = controller.HasCookie("OAuth-Token");
  83. if (hasApiKey || hasOAuthTokenInCookie ||
  84. string.IsNullOrEmpty(authHeader) == false && authHeader.StartsWith("Bearer "))
  85. {
  86. return oAuthRequestAuthorizer.TryAuthorize(controller, hasApiKey, IgnoreDb.Urls.Contains(requestUrl), out msg);
  87. }
  88. return windowsRequestAuthorizer.TryAuthorize(controller, IgnoreDb.Urls.Contains(requestUrl), out msg);
  89. }
  90. public bool TryAuthorizeSingleUseAuthToken(string token, string tenantName, out object msg, out HttpStatusCode statusCode, out IPrincipal user)
  91. {
  92. user = null;
  93. OneTimeToken value;
  94. if (singleUseAuthTokens.TryRemove(token, out value) == false)
  95. {
  96. msg = new
  97. {
  98. Error = "Unknown single use token, maybe it was already used?"
  99. };
  100. statusCode = HttpStatusCode.Forbidden;
  101. return false;
  102. }
  103. if (string.Equals(value.ResourceName, tenantName, StringComparison.InvariantCultureIgnoreCase) == false &&
  104. (value.ResourceName == Constants.SystemDatabase && tenantName == null) == false)
  105. {
  106. msg = new
  107. {
  108. Error = "This single use token cannot be used for this resource!"
  109. };
  110. statusCode = HttpStatusCode.Forbidden;
  111. return false;
  112. }
  113. if (value.Age.TotalMinutes > 2.5) // if the value is over 2.5 minutes old, reject it
  114. {
  115. msg = new
  116. {
  117. Error = "This single use token has expired after " + value.Age.TotalSeconds + " seconds"
  118. };
  119. statusCode = HttpStatusCode.Forbidden;
  120. return false;
  121. }
  122. msg = null;
  123. statusCode = HttpStatusCode.OK;
  124. CurrentOperationContext.User.Value = user = value.User;
  125. return true;
  126. }
  127. private bool TryAuthorizeSingleUseAuthToken(RavenBaseApiController controller, string token, out HttpResponseMessage msg)
  128. {
  129. if (controller.WasAlreadyAuthorizedUsingSingleAuthToken)
  130. {
  131. msg = controller.GetEmptyMessage();
  132. return true;
  133. }
  134. object result;
  135. HttpStatusCode statusCode;
  136. IPrincipal user;
  137. var resourceName = controller.ResourceName == null ? null : controller.ResourcePrefix + controller.ResourceName;
  138. var success = TryAuthorizeSingleUseAuthToken(token, resourceName, out result, out statusCode, out user);
  139. controller.User = user;
  140. msg = success == false ? controller.GetMessageWithObject(result, statusCode) : controller.GetEmptyMessage();
  141. controller.WasAlreadyAuthorizedUsingSingleAuthToken = success;
  142. return success;
  143. }
  144. public IPrincipal GetUser(RavenBaseApiController controller)
  145. {
  146. if (controller.WasAlreadyAuthorizedUsingSingleAuthToken)
  147. {
  148. return controller.User;
  149. }
  150. var hasApiKey = "True".Equals(controller.GetQueryStringValue("Has-Api-Key"), StringComparison.CurrentCultureIgnoreCase);
  151. var authHeader = controller.GetHeader("Authorization");
  152. var hasOAuthTokenInCookie = controller.HasCookie("OAuth-Token");
  153. if (hasApiKey || hasOAuthTokenInCookie ||
  154. string.IsNullOrEmpty(authHeader) == false && authHeader.StartsWith("Bearer "))
  155. {
  156. return oAuthRequestAuthorizer.GetUser(controller, hasApiKey);
  157. }
  158. return windowsRequestAuthorizer.GetUser(controller);
  159. }
  160. public List<string> GetApprovedResources(IPrincipal user, BaseDatabaseApiController controller, string[] databases)
  161. {
  162. var authHeader = controller.GetHeader("Authorization");
  163. List<string> approved;
  164. if (string.IsNullOrEmpty(authHeader) == false && authHeader.StartsWith("Bearer "))
  165. approved = oAuthRequestAuthorizer.GetApprovedResources(user);
  166. else
  167. approved = windowsRequestAuthorizer.GetApprovedResources(user);
  168. if (approved.Contains("*"))
  169. return databases.ToList();
  170. return approved;
  171. }
  172. public List<string> GetApprovedResources(IPrincipal user, string authHeader, string[] databases)
  173. {
  174. List<string> approved;
  175. if (string.IsNullOrEmpty(authHeader) == false && authHeader.StartsWith("Bearer "))
  176. approved = oAuthRequestAuthorizer.GetApprovedResources(user);
  177. else
  178. approved = windowsRequestAuthorizer.GetApprovedResources(user);
  179. if (approved.Contains("*"))
  180. return databases.ToList();
  181. return approved;
  182. }
  183. public override void Dispose()
  184. {
  185. windowsRequestAuthorizer.Dispose();
  186. oAuthRequestAuthorizer.Dispose();
  187. }
  188. public string GenerateSingleUseAuthToken(string resourceName, IPrincipal user)
  189. {
  190. var token = new OneTimeToken
  191. {
  192. ResourceName = string.IsNullOrEmpty(resourceName)?"<system>" : resourceName,
  193. User = user
  194. };
  195. var tokenString = Guid.NewGuid().ToString();
  196. singleUseAuthTokens.TryAdd(tokenString, token);
  197. if (singleUseAuthTokens.Count > 25)
  198. {
  199. foreach (var oneTimeToken in singleUseAuthTokens.Where(x => x.Value.Age.TotalMinutes > 3))
  200. {
  201. OneTimeToken value;
  202. singleUseAuthTokens.TryRemove(oneTimeToken.Key, out value);
  203. }
  204. }
  205. return tokenString;
  206. }
  207. }
  208. public class OneTimeTokenPrincipal : IPrincipal, IIdentity
  209. {
  210. public bool IsInRole(string role)
  211. {
  212. if (role == "Administrators")
  213. {
  214. return IsAdministratorInAnonymouseMode;
  215. }
  216. return false;
  217. }
  218. public bool IsAdministratorInAnonymouseMode { get; set; }
  219. public IIdentity Identity { get { return this; } }
  220. public string Name { get; set; }
  221. public string AuthenticationType { get { return "one-time-token"; } }
  222. public bool IsAuthenticated { get { return true; } }
  223. }
  224. }