/Raven.Database/Server/Security/OAuth/OAuthRequestAuthorizer.cs

https://github.com/samueldjack/ravendb · C# · 173 lines · 140 code · 33 blank · 0 comment · 28 complexity · 636c7563e312b97b92a08a8309f42de0 MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Security.Principal;
  4. using System.Linq;
  5. using Raven.Abstractions.Data;
  6. using Raven.Database.Server.Abstractions;
  7. using Raven.Database.Extensions;
  8. namespace Raven.Database.Server.Security.OAuth
  9. {
  10. public class OAuthRequestAuthorizer : AbstractRequestAuthorizer
  11. {
  12. public bool Authorize(IHttpContext ctx, bool hasApiKey)
  13. {
  14. var httpRequest = ctx.Request;
  15. var isGetRequest = IsGetRequest(httpRequest.HttpMethod, httpRequest.Url.AbsolutePath);
  16. var allowUnauthenticatedUsers = // we need to auth even if we don't have to, for bundles that want the user
  17. Settings.AnonymousUserAccessMode == AnonymousUserAccessMode.All ||
  18. Settings.AnonymousUserAccessMode == AnonymousUserAccessMode.Admin ||
  19. Settings.AnonymousUserAccessMode == AnonymousUserAccessMode.Get &&
  20. isGetRequest;
  21. var token = GetToken(ctx);
  22. if (token == null)
  23. {
  24. if (allowUnauthenticatedUsers)
  25. return true;
  26. WriteAuthorizationChallenge(ctx, hasApiKey ? 412 : 401, "invalid_request", "The access token is required");
  27. return false;
  28. }
  29. AccessTokenBody tokenBody;
  30. if (!AccessToken.TryParseBody(Settings.OAuthTokenKey, token, out tokenBody))
  31. {
  32. if (allowUnauthenticatedUsers)
  33. return true;
  34. WriteAuthorizationChallenge(ctx, 401, "invalid_token", "The access token is invalid");
  35. return false;
  36. }
  37. if (tokenBody.IsExpired())
  38. {
  39. if (allowUnauthenticatedUsers)
  40. return true;
  41. WriteAuthorizationChallenge(ctx, 401, "invalid_token", "The access token is expired");
  42. return false;
  43. }
  44. var writeAccess = isGetRequest == false;
  45. if(!tokenBody.IsAuthorized(TenantId, writeAccess))
  46. {
  47. if (allowUnauthenticatedUsers)
  48. return true;
  49. WriteAuthorizationChallenge(ctx, 403, "insufficient_scope",
  50. writeAccess ?
  51. "Not authorized for read/write access for tenant " + TenantId :
  52. "Not authorized for tenant " + TenantId);
  53. return false;
  54. }
  55. ctx.User = new OAuthPrincipal(tokenBody, TenantId);
  56. CurrentOperationContext.Headers.Value[Constants.RavenAuthenticatedUser] = tokenBody.UserId;
  57. CurrentOperationContext.User.Value = ctx.User;
  58. return true;
  59. }
  60. public override List<string> GetApprovedDatabases(IHttpContext context)
  61. {
  62. var user = context.User as OAuthPrincipal;
  63. if(user == null)
  64. return new List<string>();
  65. return user.GetApprovedDatabases();
  66. }
  67. public override void Dispose()
  68. {
  69. }
  70. static string GetToken(IHttpContext ctx)
  71. {
  72. const string bearerPrefix = "Bearer ";
  73. var auth = ctx.Request.Headers["Authorization"];
  74. if(auth == null)
  75. {
  76. auth = ctx.Request.GetCookie("OAuth-Token");
  77. if (auth != null)
  78. auth = Uri.UnescapeDataString(auth);
  79. }
  80. if (auth == null || auth.Length <= bearerPrefix.Length ||
  81. !auth.StartsWith(bearerPrefix, StringComparison.OrdinalIgnoreCase))
  82. return null;
  83. var token = auth.Substring(bearerPrefix.Length, auth.Length - bearerPrefix.Length);
  84. return token;
  85. }
  86. void WriteAuthorizationChallenge(IHttpContext ctx, int statusCode, string error, string errorDescription)
  87. {
  88. if (string.IsNullOrEmpty(Settings.OAuthTokenServer) == false)
  89. {
  90. ctx.Response.AddHeader("OAuth-Source", Settings.OAuthTokenServer);
  91. }
  92. ctx.Response.StatusCode = statusCode;
  93. ctx.Response.AddHeader("WWW-Authenticate", string.Format("Bearer realm=\"Raven\", error=\"{0}\",error_description=\"{1}\"", error, errorDescription));
  94. }
  95. }
  96. public class OAuthPrincipal : IPrincipal, IIdentity
  97. {
  98. private readonly AccessTokenBody tokenBody;
  99. private readonly string tenantId;
  100. public OAuthPrincipal(AccessTokenBody tokenBody, string tenantId)
  101. {
  102. this.tokenBody = tokenBody;
  103. this.tenantId = tenantId;
  104. }
  105. public bool IsInRole(string role)
  106. {
  107. if ("Administrators".Equals(role, StringComparison.OrdinalIgnoreCase) == false)
  108. return false;
  109. var databaseAccess = tokenBody.AuthorizedDatabases
  110. .Where(x=>
  111. string.Equals(x.TenantId, tenantId, StringComparison.OrdinalIgnoreCase) ||
  112. x.TenantId == "*");
  113. return databaseAccess.Any(access => access.Admin);
  114. }
  115. public IIdentity Identity
  116. {
  117. get { return this; }
  118. }
  119. public string Name
  120. {
  121. get { return tokenBody.UserId; }
  122. }
  123. public string AuthenticationType
  124. {
  125. get { return "OAuth"; }
  126. }
  127. public bool IsAuthenticated
  128. {
  129. get { return true; }
  130. }
  131. public List<string> GetApprovedDatabases()
  132. {
  133. return tokenBody.AuthorizedDatabases.Select(access => access.TenantId).ToList();
  134. }
  135. public AccessTokenBody TokenBody
  136. {
  137. get { return tokenBody; }
  138. }
  139. }
  140. }