1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
5 * This software is subject to the Microsoft Public License (Ms-PL).
\r
6 * A copy of the license can be found in the license.htm file included
\r
7 * in this distribution.
\r
9 * You must not remove this notice, or any other, from this software.
\r
11 * ***************************************************************************/
\r
13 namespace System.Web.Mvc {
\r
15 using System.Diagnostics.CodeAnalysis;
\r
17 using System.Security.Principal;
\r
20 [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes",
\r
21 Justification = "Unsealed so that subclassed types can set properties in the default constructor or override our behavior.")]
\r
22 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
\r
23 public class AuthorizeAttribute : FilterAttribute, IAuthorizationFilter {
\r
25 private string _roles;
\r
26 private string[] _rolesSplit = new string[0];
\r
27 private string _users;
\r
28 private string[] _usersSplit = new string[0];
\r
30 public string Roles {
\r
32 return _roles ?? String.Empty;
\r
36 _rolesSplit = SplitString(value);
\r
40 public string Users {
\r
42 return _users ?? String.Empty;
\r
46 _usersSplit = SplitString(value);
\r
50 // This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
\r
51 protected virtual bool AuthorizeCore(HttpContextBase httpContext) {
\r
52 if (httpContext == null) {
\r
53 throw new ArgumentNullException("httpContext");
\r
56 IPrincipal user = httpContext.User;
\r
57 if (!user.Identity.IsAuthenticated) {
\r
61 if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {
\r
65 if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {
\r
72 private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
\r
73 validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
\r
76 public virtual void OnAuthorization(AuthorizationContext filterContext) {
\r
77 if (filterContext == null) {
\r
78 throw new ArgumentNullException("filterContext");
\r
81 if (AuthorizeCore(filterContext.HttpContext)) {
\r
83 // Since we're performing authorization at the action level, the authorization code runs
\r
84 // after the output caching module. In the worst case this could allow an authorized user
\r
85 // to cause the page to be cached, then an unauthorized user would later be served the
\r
86 // cached page. We work around this by telling proxies not to cache the sensitive page,
\r
87 // then we hook our custom authorization code into the caching mechanism so that we have
\r
88 // the final say on whether a page should be served from the cache.
\r
90 HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
\r
91 cachePolicy.SetProxyMaxAge(new TimeSpan(0));
\r
92 cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
\r
95 // auth failed, redirect to login page
\r
96 filterContext.Result = new HttpUnauthorizedResult();
\r
100 // This method must be thread-safe since it is called by the caching module.
\r
101 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) {
\r
102 if (httpContext == null) {
\r
103 throw new ArgumentNullException("httpContext");
\r
106 bool isAuthorized = AuthorizeCore(httpContext);
\r
107 return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
\r
110 internal static string[] SplitString(string original) {
\r
111 if (String.IsNullOrEmpty(original)) {
\r
112 return new string[0];
\r
115 var split = from piece in original.Split(',')
\r
116 let trimmed = piece.Trim()
\r
117 where !String.IsNullOrEmpty(trimmed)
\r
119 return split.ToArray();
\r