Added attribute driven security on Behavior methods
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 6.1k
- Forks
- 1.7k
- PR merge metrics
- No merged PRs in 30d
Description
Hey,
I didnt know where to post my code to help improve the project.
I added an attribute driven security control on Behavior methods :
- Usage of Castle Dynamic proxies
- Creation of an AuthorizeAttribute class
- usage of Castle IInterceptor
in WebSocketServiceManager class :
using Castle.DynamicProxy;
...
public void AddService<TBehavior>(
string path, Action<TBehavior> initializer
)
where TBehavior : WebSocketBehavior, new()
{
if (path == null)
throw new ArgumentNullException("path");
if (path.Length == 0)
throw new ArgumentException("An empty string.", "path");
if (path[0] != '/')
throw new ArgumentException("Not an absolute path.", "path");
if (path.IndexOfAny(new[] { '?', '#' }) > -1)
{
var msg = "It includes either or both query and fragment components.";
throw new ArgumentException(msg, "path");
}
path = path.TrimSlashFromEnd();
lock (_sync)
{
WebSocketServiceHost host;
if (_hosts.TryGetValue(path, out host))
throw new ArgumentException("Already in use.", "path");
// ***** create host *****
host = new WebSocketServiceHost<TBehavior>(
path, () => this.CreateBehavior<TBehavior>(), initializer, _log
);
if (!_clean)
host.KeepClean = false;
if (_waitTime != host.WaitTime)
host.WaitTime = _waitTime;
if (_state == ServerState.Start)
host.Start();
_hosts.Add(path, host);
}
}
protected T CreateBehavior<T>()
{
// ***** prepare dynamic proxy *****
ProxyGenerator generator = new ProxyGenerator();
T proxy = (T)generator.CreateClassProxy(typeof(T), new AuthorizeInterceptor());
return proxy;
}
new AuthorizeAttribute class :
using System;
namespace WebSocketSharp.Server.Attributes
{
[AttributeUsage(AttributeTargets.All)]
public class AuthorizeAttribute : Attribute
{
public AuthorizeAttribute()
{
}
public string Roles { get; set; }
}
}
new AuthorizeInterceptor class :
using System;
using System.Linq;
using System.Reflection;
using Castle.DynamicProxy;
using WebSocketSharp.Server.Attributes;
using WebSocketSharp.Server.Exceptions;
namespace WebSocketSharp.Server.Interceptors
{
public class AuthorizeInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
// ***** check attribute *****
AuthorizeAttribute attr = invocation.Method.GetCustomAttributes<AuthorizeAttribute>(true).FirstOrDefault();
if (attr == null || string.IsNullOrWhiteSpace(attr.Roles))
{
invocation.Proceed();
return;
}
// ***** check instance *****
if (!(invocation.InvocationTarget is WebSocketBehavior))
{
invocation.Proceed();
return;
}
WebSocketBehavior behavior = (WebSocketBehavior)invocation.InvocationTarget;
// ***** check user authenticated *****
if (!behavior.Context.IsAuthenticated)
{
throw new UnauthenticatedException($"Cannot run method \"{invocation.Method.Name}\". No user authenticated");
}
// ***** extract roles *****
string[] roles = attr.Roles.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
if (roles.Length == 0)
{
invocation.Proceed();
return;
}
// ***** check user in role *****
bool hasRole = false;
foreach (string role in roles)
{
hasRole |= behavior.Context.User.IsInRole(role);
}
if (!hasRole)
{
throw new UnauthorizedException($"Cannot run method \"{invocation.Method.Name}\". Unauthorized user => should have \"{attr.Roles}\"");
}
// ***** execute *****
invocation.Proceed();
}
}
}
I let you create the exceptions as Exception with all constructors overridden ;o)
I use it like this in my Behavior :
[Authorize(Roles = "toto, titi")]
protected override void OnMessage(MessageEventArgs e)
{
...
}
Cheers
Christophe
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing WebSocketServiceManager.CreateBehavior and the proposed Castle DynamicProxy integration, then examine AuthorizeAttribute and AuthorizeInterceptor with WebSocketBehavior. Done means agreeing on how attributed methods enforce authentication and roles, including the proposed unauthorized and unauthenticated exceptions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100