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 readonly object _typeId = new object();
\r
27 private string _roles;
\r
28 private string[] _rolesSplit = new string[0];
\r
29 private string _users;
\r
30 private string[] _usersSplit = new string[0];
\r
32 public string Roles {
\r
34 return _roles ?? String.Empty;
\r
38 _rolesSplit = SplitString(value);
\r
42 public override object TypeId {
\r
48 public string Users {
\r
50 return _users ?? String.Empty;
\r
54 _usersSplit = SplitString(value);
\r
58 // This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method.
\r
59 protected virtual bool AuthorizeCore(HttpContextBase httpContext) {
\r
60 if (httpContext == null) {
\r
61 throw new ArgumentNullException("httpContext");
\r
64 IPrincipal user = httpContext.User;
\r
65 if (!user.Identity.IsAuthenticated) {
\r
69 if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) {
\r
73 if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole)) {
\r
80 private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus) {
\r
81 validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
\r
84 public virtual void OnAuthorization(AuthorizationContext filterContext) {
\r
85 if (filterContext == null) {
\r
86 throw new ArgumentNullException("filterContext");
\r
89 if (AuthorizeCore(filterContext.HttpContext)) {
\r
91 // Since we're performing authorization at the action level, the authorization code runs
\r
92 // after the output caching module. In the worst case this could allow an authorized user
\r
93 // to cause the page to be cached, then an unauthorized user would later be served the
\r
94 // cached page. We work around this by telling proxies not to cache the sensitive page,
\r
95 // then we hook our custom authorization code into the caching mechanism so that we have
\r
96 // the final say on whether a page should be served from the cache.
\r
98 HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
\r
99 cachePolicy.SetProxyMaxAge(new TimeSpan(0));
\r
100 cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
\r
103 HandleUnauthorizedRequest(filterContext);
\r
107 protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
\r
108 // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
\r
109 filterContext.Result = new HttpUnauthorizedResult();
\r
112 // This method must be thread-safe since it is called by the caching module.
\r
113 protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext) {
\r
114 if (httpContext == null) {
\r
115 throw new ArgumentNullException("httpContext");
\r
118 bool isAuthorized = AuthorizeCore(httpContext);
\r
119 return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
\r
122 internal static string[] SplitString(string original) {
\r
123 if (String.IsNullOrEmpty(original)) {
\r
124 return new string[0];
\r
127 var split = from piece in original.Split(',')
\r
128 let trimmed = piece.Trim()
\r
129 where !String.IsNullOrEmpty(trimmed)
\r
131 return split.ToArray();
\r