Files
linux-packaging-mono/external/aspnetwebstack/src/System.Web.Http/AuthorizeAttribute.cs
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

180 lines
7.0 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Principal;
using System.Threading;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
namespace System.Web.Http
{
/// <summary>
/// An authorization filter that verifies the request's <see cref="IPrincipal"/>.
/// </summary>
/// <remarks>You can declare multiple of these attributes per action. You can also use <see cref="AllowAnonymousAttribute"/>
/// to disable authorization for a specific action.</remarks>
/// <seealso cref="M:AuthorizeCore"/>
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want to support extensibility")]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : AuthorizationFilterAttribute
{
private static readonly string[] _emptyArray = new string[0];
private readonly object _typeId = new object();
private string _roles;
private string[] _rolesSplit = _emptyArray;
private string _users;
private string[] _usersSplit = _emptyArray;
/// <summary>
/// Gets or sets the authorized roles.
/// </summary>
/// <value>
/// The roles string.
/// </value>
/// <remarks>Multiple role names can be specified using the comma character as a separator.</remarks>
public string Roles
{
get { return _roles ?? String.Empty; }
set
{
_roles = value;
_rolesSplit = SplitString(value);
}
}
/// <summary>
/// Gets a unique identifier for this <see cref="T:System.Attribute"/>.
/// </summary>
/// <returns>The unique identifier for the attribute.</returns>
public override object TypeId
{
get { return _typeId; }
}
/// <summary>
/// Gets or sets the authorized users.
/// </summary>
/// <value>
/// The users string.
/// </value>
/// <remarks>Multiple role names can be specified using the comma character as a separator.</remarks>
public string Users
{
get { return _users ?? String.Empty; }
set
{
_users = value;
_usersSplit = SplitString(value);
}
}
/// <summary>
/// Determines whether access for this particular request is authorized. This method uses the user <see cref="IPrincipal"/>
/// returned via <see cref="System.Threading.Thread.CurrentPrincipal"/>. Authorization is denied if the user is not authenticated,
/// the user is not in the authorized group of <see cref="P:Users"/> (if defined), or if the user is not in any of the authorized
/// <see cref="P:Roles"/> (if defined).
/// </summary>
/// <returns><c>true</c> if access is authorized; otherwise <c>false</c>.</returns>
private bool AuthorizeCore()
{
IPrincipal user = Thread.CurrentPrincipal;
if (user == null || !user.Identity.IsAuthenticated)
{
return false;
}
if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
{
return false;
}
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
return true;
}
/// <summary>
/// Called when an action is being authorized. This method uses the user <see cref="IPrincipal"/>
/// returned via <see cref="M:HttpRequestMessageExtensions.GetUserPrincipal"/>. Authorization is denied if
/// - the request is not associated with any user.
/// - the user is not authenticated,
/// - the user is authenticated but is not in the authorized group of <see cref="P:Users"/> (if defined), or if the user
/// is not in any of the authorized <see cref="P:Roles"/> (if defined).
///
/// If authorization is denied then this method will invoke <see cref="M:HandleUnauthorizedRequest"/> to process the unauthorized request.
/// </summary>
/// <remarks>You can use <see cref="AllowAnonymousAttribute"/> to cause authorization checks to be skipped for a particular
/// action or controller.</remarks>
/// <param name="actionContext">The context.</param>
/// <exception cref="ArgumentNullException">The context parameter is null.</exception>
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw Error.ArgumentNull("actionContext");
}
if (SkipAuthorization(actionContext))
{
return;
}
if (!AuthorizeCore())
{
HandleUnauthorizedRequest(actionContext);
}
}
/// <summary>
/// Processes requests that fail authorization. This default implementation creates a new response with the
/// Unauthorized status code. Override this method to provide your own handling for unauthorized requests.
/// </summary>
/// <param name="actionContext">The context.</param>
protected virtual void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw Error.ArgumentNull("actionContext");
}
actionContext.Response = actionContext.ControllerContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
private static bool SkipAuthorization(HttpActionContext actionContext)
{
Contract.Assert(actionContext != null);
return actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()
|| actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
}
/// <summary>
/// Splits the string on commas and removes any leading/trailing whitespace from each result item.
/// </summary>
/// <param name="original">The input string.</param>
/// <returns>An array of strings parsed from the input <paramref name="original"/> string.</returns>
internal static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return _emptyArray;
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}