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.
Lidarr/src/Microsoft.AspNet.SignalR.Core/AuthorizeAttribute.cs

161 lines
6.7 KiB

// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.md in the project root for license information.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Principal;
using Microsoft.AspNet.SignalR.Hubs;
namespace Microsoft.AspNet.SignalR
{
/// <summary>
/// Apply to Hubs and Hub methods to authorize client connections to Hubs and authorize client invocations of Hub methods.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "MVC and WebAPI don't seal their AuthorizeAttributes")]
public class AuthorizeAttribute : Attribute, IAuthorizeHubConnection, IAuthorizeHubMethodInvocation
{
private string _roles;
private string[] _rolesSplit = new string[0];
private string _users;
private string[] _usersSplit = new string[0];
[SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Already somewhat represented by set-only RequiredOutgoing property.")]
protected bool? _requireOutgoing;
/// <summary>
/// Set to false to apply authorization only to the invocations of any of the Hub's server-side methods.
/// This property only affects attributes applied to the Hub class.
/// This property cannot be read.
/// </summary>
[SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Justification = "Must be property because this is an attribute parameter.")]
public bool RequireOutgoing
{
// It is impossible to tell here whether the attribute is being applied to a method or class. This makes
// it impossible to determine whether the value should be true or false when _requireOutgoing is null.
// It is also impossible to have a Nullable attribute parameter type.
get { throw new NotImplementedException(Resources.Error_DoNotReadRequireOutgoing); }
set { _requireOutgoing = value; }
}
/// <summary>
/// Gets or sets the user roles.
/// </summary>
public string Roles
{
get { return _roles ?? String.Empty; }
set
{
_roles = value;
_rolesSplit = SplitString(value);
}
}
/// <summary>
/// Gets or sets the authorized users.
/// </summary>
public string Users
{
get { return _users ?? String.Empty; }
set
{
_users = value;
_usersSplit = SplitString(value);
}
}
/// <summary>
/// Determines whether client is authorized to connect to <see cref="IHub"/>.
/// </summary>
/// <param name="hubDescriptor">Description of the hub client is attempting to connect to.</param>
/// <param name="request">The (re)connect request from the client.</param>
/// <returns>true if the caller is authorized to connect to the hub; otherwise, false.</returns>
public virtual bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
if (request == null)
{
throw new ArgumentNullException("request");
}
// If RequireOutgoing is explicitly set to false, authorize all connections.
if (_requireOutgoing.HasValue && !_requireOutgoing.Value)
{
return true;
}
return UserAuthorized(request.User);
}
/// <summary>
/// Determines whether client is authorized to invoke the <see cref="IHub"/> method.
/// </summary>
/// <param name="hubIncomingInvokerContext">An <see cref="IHubIncomingInvokerContext"/> providing details regarding the <see cref="IHub"/> method invocation.</param>
/// <param name="appliesToMethod">Indicates whether the interface instance is an attribute applied directly to a method.</param>
/// <returns>true if the caller is authorized to invoke the <see cref="IHub"/> method; otherwise, false.</returns>
public virtual bool AuthorizeHubMethodInvocation(IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
if (hubIncomingInvokerContext == null)
{
throw new ArgumentNullException("hubIncomingInvokerContext");
}
// It is impossible to require outgoing auth at the method level with SignalR's current design.
// Even though this isn't the stage at which outgoing auth would be applied, we want to throw a runtime error
// to indicate when the attribute is being used with obviously incorrect expectations.
// We must explicitly check if _requireOutgoing is true since it is a Nullable type.
if (appliesToMethod && (_requireOutgoing == true))
{
throw new ArgumentException(Resources.Error_MethodLevelOutgoingAuthorization);
}
return UserAuthorized(hubIncomingInvokerContext.Hub.Context.User);
}
/// <summary>
/// When overridden, provides an entry point for custom authorization checks.
/// Called by <see cref="AuthorizeAttribute.AuthorizeHubConnection"/> and <see cref="AuthorizeAttribute.AuthorizeHubMethodInvocation"/>.
/// </summary>
/// <param name="user">The <see cref="System.Security.Principal.IPrincipal"/> for the client being authorize</param>
/// <returns>true if the user is authorized, otherwise, false</returns>
protected virtual bool UserAuthorized(IPrincipal user)
{
if (user == null)
{
return false;
}
if (!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;
}
private static string[] SplitString(string original)
{
if (String.IsNullOrEmpty(original))
{
return new string[0];
}
var split = from piece in original.Split(',')
let trimmed = piece.Trim()
where !String.IsNullOrEmpty(trimmed)
select trimmed;
return split.ToArray();
}
}
}