Skip to content

Commit

Permalink
SendBotPlugins + token security
Browse files Browse the repository at this point in the history
SendBotPlugins websocket event added
TokenFilter to only allow
  • Loading branch information
chsami committed Feb 25, 2024
1 parent a92e8a7 commit a86b305
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 14 deletions.
6 changes: 6 additions & 0 deletions MicrobotApi/ConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MicrobotApi;

public class ConnectionManager
{
public readonly List<string> Connections = [];
}
15 changes: 9 additions & 6 deletions MicrobotApi/ChatHub.cs → MicrobotApi/MicrobotHub.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using Microsoft.AspNetCore.SignalR;
using MicrobotApi.Models;
using Microsoft.AspNetCore.SignalR;

namespace MicrobotApi;

public class ChatHub : Hub
public class MicrobotHub : Hub
{
private readonly static List<string> _connections =
new List<string>();

public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user,message);
Expand All @@ -15,7 +13,6 @@ public async Task SendMessage(string user, string message)
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);

await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
}

Expand All @@ -25,4 +22,10 @@ public async Task RemoveFromGroup(string groupName)

await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has left the group {groupName}.");
}

public async Task SendBotPlugins(SendBotPluginRequestModel sendBotPluginRequestModel)
{
await Clients.Group(sendBotPluginRequestModel.Group)
.SendAsync("ReceiveBotPlugins", sendBotPluginRequestModel.Plugins);
}
}
6 changes: 6 additions & 0 deletions MicrobotApi/Models/IHubRequestModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MicrobotApi.Models;

public interface IHubRequestModel
{
public string Group { get; set; }
}
7 changes: 7 additions & 0 deletions MicrobotApi/Models/PluginRequestModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MicrobotApi.Models;

public class PluginRequestModel
{
public string Name { get; set; }
public bool Active { get; set; }
}
7 changes: 7 additions & 0 deletions MicrobotApi/Models/SendBotPluginRequestModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MicrobotApi.Models;

public class SendBotPluginRequestModel : IHubRequestModel
{
public string Group { get; set; }
public List<PluginRequestModel> Plugins { get; set; }
}
30 changes: 22 additions & 8 deletions MicrobotApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using MicrobotApi;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

var builder = WebApplication.CreateBuilder(args);

Expand All @@ -21,7 +23,15 @@
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();
builder.Services
.AddSignalR()
.AddHubOptions<MicrobotHub>(options =>
{
// Local filters will run second
options.AddFilter<TokenFilter>();
});

builder.Services.AddSingleton<ConnectionManager>();

var app = builder.Build();

Expand All @@ -37,16 +47,20 @@
app.MapGet("/token", () =>
{
var token = Guid.NewGuid();
var connectionManager = app.Services.GetService<ConnectionManager>();
connectionManager?.Connections.Add(token.ToString());
return token;
})
.WithName("getToken")
.WithOpenApi();
app.MapPost("/token", async ([FromBody] TokenRequestModel tokenRequestModel) =>
{
var connectionManager = app.Services.GetService<ConnectionManager>();
return connectionManager?.Connections.Contains(tokenRequestModel.Token);
})
.WithName("Check Token Validity")
.WithOpenApi();
app.UseCors();
app.MapHub<ChatHub>("/microbot");

app.Run();
app.MapHub<MicrobotHub>("/microbot");

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
app.Run();
67 changes: 67 additions & 0 deletions MicrobotApi/TokenFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using Microsoft.AspNetCore.SignalR;

namespace MicrobotApi;

public class TokenFilter : IHubFilter
{
private readonly ConnectionManager _connectionManager;
private const string invalidTokenMessage = "Invalid token!";

public TokenFilter(ConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public async ValueTask<object> InvokeMethodAsync(
HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
{

var token = GetToken(invocationContext.Context);

var exists = _connectionManager.Connections.Contains(token);
if (!exists)
throw new UnauthorizedAccessException(invalidTokenMessage);

Console.WriteLine($"Calling hub method '{invocationContext.HubMethodName}'");
try
{
return await next(invocationContext);
}
catch (Exception ex)
{
Console.WriteLine($"Exception calling '{invocationContext.HubMethodName}': {ex}");
throw;
}
}

// Optional method
public Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
{
return next(context);
}

// Optional method
public Task OnDisconnectedAsync(
HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
{
return next(context, exception);
}

private string? GetToken(HubCallerContext context)
{
var httpContext = context.GetHttpContext();

var validHttpRequest = httpContext is { Request.Headers: not null } && httpContext.Request.Headers.Any();

if (!validHttpRequest
|| (string.IsNullOrWhiteSpace(httpContext?.Request.Headers?["token"])
&& string.IsNullOrWhiteSpace(httpContext?.Request.Query["token"].FirstOrDefault())))
{
return null;
}

var token = httpContext.Request.Headers?["token"].FirstOrDefault()
?? httpContext.Request.Query["token"].FirstOrDefault();

return token;
}
}
6 changes: 6 additions & 0 deletions MicrobotApi/TokenRequestModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace MicrobotApi;

public class TokenRequestModel
{
public String Token { get; set; }
}

0 comments on commit a86b305

Please sign in to comment.