The next generation of the Teknik Services. Written in ASP.NET. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TeknikAuthorizeAttribute.cs 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using System.Web.Routing;
  7. using Teknik.Areas.Error.Controllers;
  8. using Teknik.Utilities;
  9. using Teknik.Areas.Users.Controllers;
  10. using Teknik.Models;
  11. using Teknik.Areas.Users.Utility;
  12. using Teknik.Areas.Users.Models;
  13. using Teknik.Configuration;
  14. namespace Teknik.Attributes
  15. {
  16. public enum AuthType
  17. {
  18. Basic,
  19. Forms
  20. }
  21. [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
  22. public class TeknikAuthorizeAttribute : AuthorizeAttribute
  23. {
  24. private AuthType m_AuthType { get; set; }
  25. public TeknikAuthorizeAttribute() : this(AuthType.Forms)
  26. {
  27. }
  28. public TeknikAuthorizeAttribute(AuthType authType)
  29. {
  30. m_AuthType = authType;
  31. }
  32. public override void OnAuthorization(AuthorizationContext filterContext)
  33. {
  34. if (filterContext == null)
  35. {
  36. throw new ArgumentNullException("filterContext");
  37. }
  38. if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
  39. {
  40. // If a child action cache block is active, we need to fail immediately, even if authorization
  41. // would have succeeded. The reason is that there's no way to hook a callback to rerun
  42. // authorization before the fragment is served from the cache, so we can't guarantee that this
  43. // filter will be re-run on subsequent requests.
  44. throw new InvalidOperationException("AuthorizeAttribute cannot be used within a child action caching block.");
  45. }
  46. // Check to see if we want to skip Authentication Check
  47. bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)
  48. || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);
  49. if (skipAuthorization)
  50. return;
  51. // Check the users auth
  52. if (AuthorizeCore(filterContext.HttpContext))
  53. {
  54. // ** IMPORTANT **
  55. // Since we're performing authorization at the action level, the authorization code runs
  56. // after the output caching module. In the worst case this could allow an authorized user
  57. // to cause the page to be cached, then an unauthorized user would later be served the
  58. // cached page. We work around this by telling proxies not to cache the sensitive page,
  59. // then we hook our custom authorization code into the caching mechanism so that we have
  60. // the final say on whether a page should be served from the cache.
  61. HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
  62. cachePolicy.SetProxyMaxAge(new TimeSpan(0));
  63. cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
  64. return;
  65. }
  66. else if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
  67. {
  68. HandleUnauthorizedRequest(filterContext);
  69. }
  70. else
  71. {
  72. // uh oh, they are authorized, but don't have access. ABORT ABORT ABORT
  73. HandleInvalidAuthRequest(filterContext);
  74. }
  75. }
  76. protected override bool AuthorizeCore(HttpContextBase httpContext)
  77. {
  78. switch (m_AuthType)
  79. {
  80. case AuthType.Basic:
  81. #region Basic Authentication
  82. // Let's see if they have an auth token
  83. if (httpContext.Request != null)
  84. {
  85. if (httpContext.Request.Headers.HasKeys())
  86. {
  87. string auth = httpContext.Request.Headers["Authorization"];
  88. if (!string.IsNullOrEmpty(auth))
  89. {
  90. string[] parts = auth.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  91. string type = string.Empty;
  92. string value = string.Empty;
  93. if (parts.Length > 0)
  94. {
  95. type = parts[0].ToLower();
  96. }
  97. if (parts.Length > 1)
  98. {
  99. value = parts[1];
  100. }
  101. using (TeknikEntities entities = new TeknikEntities())
  102. {
  103. // Get the user information based on the auth type
  104. switch (type)
  105. {
  106. case "basic":
  107. KeyValuePair<string, string> authCreds = StringHelper.ParseBasicAuthHeader(value);
  108. bool validToken = UserHelper.UserTokenCorrect(entities, authCreds.Key, authCreds.Value);
  109. if (validToken)
  110. {
  111. User user = UserHelper.GetUserFromToken(entities, authCreds.Key, authCreds.Value);
  112. return UserHelper.UserHasRoles(entities, user, Roles);
  113. }
  114. break;
  115. default:
  116. break;
  117. }
  118. }
  119. }
  120. }
  121. }
  122. #endregion
  123. return false;
  124. case AuthType.Forms:
  125. return base.AuthorizeCore(httpContext);
  126. default:
  127. return base.AuthorizeCore(httpContext);
  128. }
  129. }
  130. protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
  131. {
  132. ActionResult result = new HttpUnauthorizedResult();
  133. switch (m_AuthType)
  134. {
  135. case AuthType.Basic:
  136. Config config = Config.Load();
  137. filterContext.HttpContext.Response.Clear();
  138. filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", config.Title));
  139. filterContext.HttpContext.Response.StatusCode = 401;
  140. filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
  141. filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
  142. result = new JsonResult() { Data = new { error = new { type = 401, message = "Unauthorized" } }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
  143. break;
  144. case AuthType.Forms:
  145. var userController = new UserController();
  146. if (userController != null)
  147. {
  148. // auth failed, redirect to login page
  149. var request = filterContext.HttpContext.Request;
  150. string redirectUrl = (request.Url != null) ? filterContext.HttpContext.Request.Url.AbsoluteUri.ToString() : string.Empty;
  151. result = userController.Login(redirectUrl);
  152. }
  153. break;
  154. default:
  155. break;
  156. }
  157. filterContext.Result = result;
  158. }
  159. protected void HandleInvalidAuthRequest(AuthorizationContext filterContext)
  160. {
  161. ActionResult result = new HttpUnauthorizedResult();
  162. switch (m_AuthType)
  163. {
  164. case AuthType.Basic:
  165. filterContext.HttpContext.Response.Clear();
  166. filterContext.HttpContext.Response.StatusCode = 403;
  167. filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
  168. filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
  169. result = new JsonResult() { Data = new { error = new { type = 403, message = "Not Authorized" } }, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
  170. break;
  171. case AuthType.Forms:
  172. var errorController = new ErrorController();
  173. if (errorController != null)
  174. {
  175. result = errorController.Http403(new Exception("Not Authorized"));
  176. }
  177. break;
  178. default:
  179. break;
  180. }
  181. filterContext.Result = result;
  182. }
  183. private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
  184. {
  185. validationStatus = base.OnCacheAuthorization(new HttpContextWrapper(context));
  186. }
  187. }
  188. }