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