Skip to content

Commit 2673aa6

Browse files
committed
Added SignalR for BackgroundTasks / Improved PermissionState
1 parent 1bbc44b commit 2673aa6

File tree

21 files changed

+332
-153
lines changed

21 files changed

+332
-153
lines changed

src/DragonFly.ApiKeys/Middlewares/ApiKeyMiddleware.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// https://github.com/usercode/DragonFly
33
// MIT License
44

5+
using DragonFly;
56
using Microsoft.AspNetCore.Http;
67
using System.Linq;
78
using System.Security.Claims;
@@ -35,7 +36,7 @@ public async Task Invoke(HttpContext context)
3536

3637
if (apiKey != null)
3738
{
38-
context.User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("ApiKeyId", apiKey.Id.ToString()) }, "ApiKey"));
39+
PermissionState.SetPrincipal(new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("Name", $"apikey:{apiKey.Name}"), new Claim("ApiKeyId", apiKey.Id.ToString()) }, "ApiKey")));
3940
}
4041
}
4142

src/DragonFly.ApiKeys/Services/PermissionAuthorizationService.cs

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,21 @@ public PermissionAuthorizationService(IDragonFlyApi api, MongoIdentityStore stor
3030

3131
public async Task<bool> AuthorizeAsync(ClaimsPrincipal principal, string permission)
3232
{
33-
using (new DisablePermissions())
34-
{
35-
Claim? claim = principal.FindFirst("ApiKeyId");
33+
Claim? claim = principal.FindFirst("ApiKeyId");
3634

37-
if (claim == null)
38-
{
39-
return false;
40-
}
35+
if (claim == null)
36+
{
37+
return false;
38+
}
4139

42-
Guid apikeyId = Guid.Parse(claim.Value);
40+
Guid apikeyId = Guid.Parse(claim.Value);
4341

44-
MongoApiKey apikey = await Store.ApiKeys.AsQueryable().FirstAsync(x => x.Id == apikeyId);
42+
MongoApiKey apikey = await Store.ApiKeys.AsQueryable().FirstAsync(x => x.Id == apikeyId);
4543

46-
IEnumerable<string> permissions = Api.Permission().GetPolicy(permission);
44+
IEnumerable<string> permissions = Api.Permission().GetPolicy(permission);
4745

48-
bool found = permissions.All(p => apikey.Permissions.Contains(p));
46+
bool found = permissions.All(p => apikey.Permissions.Contains(p));
4947

50-
return found;
51-
}
48+
return found;
5249
}
5350
}

src/DragonFly.App.Server/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
{
6767
await Task.Delay(TimeSpan.FromSeconds(1));
6868

69-
ctx.Task.ProgressValue += 2;
69+
await ctx.IncrementProgressValueAsync();
7070

7171
if (ctx.Task.ProgressValue >= ctx.Task.ProgressMaxValue)
7272
{

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTask.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// https://github.com/usercode/DragonFly
33
// MIT License
44

5-
using System.Threading.Tasks;
5+
using System.Security.Claims;
66

77
namespace DragonFly;
88

@@ -11,13 +11,14 @@ namespace DragonFly;
1111
/// </summary>
1212
public class BackgroundTask
1313
{
14-
public BackgroundTask(long id, string name)
14+
public BackgroundTask(long id, string name, ClaimsPrincipal? createdBy)
1515
{
1616
Id = id;
1717
Name = name;
1818
ProgressValue = 0;
1919
ProgressMaxValue = 100;
2020
CreatedAt = DateTimeOffset.Now;
21+
CreatedBy = createdBy;
2122
Status = string.Empty;
2223
State = BackgroundTaskState.Awaiting;
2324
CancellationTokenSource = new CancellationTokenSource();
@@ -38,6 +39,11 @@ public BackgroundTask(long id, string name)
3839
/// </summary>
3940
public DateTimeOffset? CreatedAt { get; }
4041

42+
/// <summary>
43+
/// CreatedBy
44+
/// </summary>
45+
public ClaimsPrincipal? CreatedBy { get; }
46+
4147
/// <summary>
4248
/// StartedAt
4349
/// </summary>
@@ -89,6 +95,11 @@ public BackgroundTask(long id, string name)
8995
/// <param name="exception"></param>
9096
public void SetException(Exception exception)
9197
{
98+
if (State != BackgroundTaskState.Running)
99+
{
100+
throw new Exception();
101+
}
102+
92103
ExitAt = DateTimeOffset.Now;
93104
State = BackgroundTaskState.Failed;
94105
Exception = exception;
@@ -99,6 +110,11 @@ public void SetException(Exception exception)
99110
/// </summary>
100111
public void SetCanceling()
101112
{
113+
if (State != BackgroundTaskState.Running)
114+
{
115+
throw new Exception();
116+
}
117+
102118
State = BackgroundTaskState.Canceling;
103119
CancellationTokenSource.Cancel();
104120
}
@@ -108,6 +124,11 @@ public void SetCanceling()
108124
/// </summary>
109125
public void SetCanceled()
110126
{
127+
if (State != BackgroundTaskState.Canceling)
128+
{
129+
throw new Exception();
130+
}
131+
111132
ExitAt = DateTimeOffset.Now;
112133
State = BackgroundTaskState.Canceled;
113134
}
@@ -117,6 +138,11 @@ public void SetCanceled()
117138
/// </summary>
118139
public void SetCompleted()
119140
{
141+
if (State != BackgroundTaskState.Running)
142+
{
143+
throw new Exception();
144+
}
145+
120146
ExitAt = DateTimeOffset.Now;
121147
State = BackgroundTaskState.Completed;
122148
ProgressValue = ProgressMaxValue;

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTaskContext.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// https://github.com/usercode/DragonFly
33
// MIT License
44

5+
using System.Runtime.CompilerServices;
6+
57
namespace DragonFly;
68

79
/// <summary>
@@ -31,19 +33,27 @@ public BackgroundTaskContext(BackgroundTask task, IServiceProvider serviceProvid
3133
/// </summary>
3234
public CancellationToken CancellationToken { get; }
3335

34-
public void SetProgress(long value)
36+
public event Func<Task>? StateChanged;
37+
38+
public async Task SetProgressAsync(long value)
3539
{
3640
Task.ProgressValue = value;
41+
42+
await StateChanged?.Invoke();
3743
}
3844

39-
public void IncrementProgressValue()
45+
public async Task IncrementProgressValueAsync()
4046
{
4147
Task.ProgressValue++;
48+
49+
await StateChanged?.Invoke();
4250
}
4351

44-
public void SetProgressMaxValue(long max)
52+
public async Task SetProgressMaxValueAsync(long max)
4553
{
4654
Task.ProgressMaxValue = max;
55+
56+
await StateChanged?.Invoke();
4757
}
4858
}
4959

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) usercode
2+
// https://github.com/usercode/DragonFly
3+
// MIT License
4+
5+
using Microsoft.AspNetCore.SignalR;
6+
7+
namespace DragonFly.AspNetCore;
8+
9+
public class BackgroundTaskHub : Hub
10+
{
11+
public override Task OnConnectedAsync()
12+
{
13+
return base.OnConnectedAsync();
14+
}
15+
16+
public override Task OnDisconnectedAsync(Exception? exception)
17+
{
18+
return base.OnDisconnectedAsync(exception);
19+
}
20+
}

src/DragonFly.AspNetCore/BackgroundTasks/BackgroundTaskManager.cs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// https://github.com/usercode/DragonFly
33
// MIT License
44

5+
using System.Security.Claims;
6+
using DragonFly.AspNetCore;
7+
using Microsoft.AspNetCore.SignalR;
58
using Microsoft.Extensions.Logging;
69

710
namespace DragonFly;
@@ -11,12 +14,18 @@ namespace DragonFly;
1114
/// </summary>
1215
public class BackgroundTaskManager : IBackgroundTaskManager
1316
{
14-
public BackgroundTaskManager(IServiceProvider serviceProvider, ILogger<BackgroundTaskManager> logger)
17+
public BackgroundTaskManager(IHubContext<BackgroundTaskHub> hub, IServiceProvider serviceProvider, ILogger<BackgroundTaskManager> logger)
1518
{
19+
Hub = hub;
1620
ServiceProvider = serviceProvider;
1721
Logger = logger;
1822
}
1923

24+
/// <summary>
25+
/// Hub
26+
/// </summary>
27+
public IHubContext<BackgroundTaskHub> Hub { get; }
28+
2029
/// <summary>
2130
/// ServiceProvider
2231
/// </summary>
@@ -45,7 +54,7 @@ public BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskConte
4554
{
4655
lock (_syncObject)
4756
{
48-
BackgroundTask backgroundTask = new BackgroundTask(_nextId++, name);
57+
BackgroundTask backgroundTask = new BackgroundTask(_nextId++, name, PermissionState.GetPrincipal());
4958

5059
backgroundTask.Task = Task.Run(async () =>
5160
{
@@ -59,6 +68,7 @@ public BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskConte
5968
backgroundTask.State = BackgroundTaskState.Running;
6069

6170
BackgroundTaskContext<T> ctx = new BackgroundTaskContext<T>(backgroundTask, ServiceProvider, input, backgroundTask.CancellationTokenSource.Token);
71+
ctx.StateChanged += () => Hub.Clients.All.SendAsync("TaskChanged");
6272

6373
await action(ctx);
6474

@@ -83,6 +93,8 @@ public BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskConte
8393
{
8494
Logger.LogInformation("Background task is exited: {id} / {name}", backgroundTask.Id, backgroundTask.Name);
8595

96+
await Hub.Clients.All.SendAsync("TaskChanged");
97+
8698
await Task.Delay(TimeSpan.FromMinutes(10));
8799

88100
lock (_syncObject)
@@ -98,31 +110,44 @@ public BackgroundTask StartNew<T>(string name, T input, Func<BackgroundTaskConte
98110
}
99111
}
100112

113+
private void Ctx_StateChanged()
114+
{
115+
throw new NotImplementedException();
116+
}
117+
101118
public Task<IEnumerable<BackgroundTaskInfo>> GetTasksAsync()
102119
{
103-
return Task.FromResult<IEnumerable<BackgroundTaskInfo>>(
104-
Tasks
105-
.Values
106-
.Select(x => new BackgroundTaskInfo() {
107-
Id = x.Id,
108-
Name = x.Name,
109-
ProgressValue = x.ProgressValue,
110-
ProgressMaxValue = x.ProgressMaxValue,
111-
Status = x.Status,
112-
State = x.State })
113-
.ToList());
120+
lock (_syncObject)
121+
{
122+
return Task.FromResult<IEnumerable<BackgroundTaskInfo>>(
123+
Tasks
124+
.Values
125+
.Select(x => new BackgroundTaskInfo()
126+
{
127+
Id = x.Id,
128+
CreatedAt = x.CreatedAt,
129+
CreatedBy = x.CreatedBy?.FindFirstValue("Name"),
130+
Name = x.Name,
131+
ProgressValue = x.ProgressValue,
132+
ProgressMaxValue = x.ProgressMaxValue,
133+
Status = x.Status,
134+
State = x.State,
135+
Exception = x.Exception?.Message
136+
})
137+
.ToList());
138+
}
114139
}
115140

116141
public Task CancelAsync(long id)
117142
{
118-
if (Tasks.TryGetValue(id, out BackgroundTask? task))
143+
lock (_syncObject)
119144
{
120-
if (task.State != BackgroundTaskState.Canceling)
145+
if (Tasks.TryGetValue(id, out BackgroundTask? task))
121146
{
122147
task.SetCanceling();
123148
}
124-
}
125149

126-
return Task.CompletedTask;
150+
return Task.CompletedTask;
151+
}
127152
}
128153
}

src/DragonFly.AspNetCore/DragonFlyBuilderExtensions.cs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.Extensions.Hosting;
1313
using DragonFly.API;
1414
using DragonFly.AspNetCore.Builders;
15+
using Microsoft.AspNetCore.Http;
1516

1617
namespace DragonFly.AspNetCore;
1718

@@ -49,7 +50,9 @@ public static IDragonFlyBuilder AddDragonFly(this IServiceCollection services)
4950

5051
services.AddSingleton<ISlugService, SlugService>();
5152

52-
services.AddHttpClient<IContentInterceptor, WebHookInterceptor>();
53+
services.AddHttpClient<IContentInterceptor, WebHookInterceptor>();
54+
55+
services.AddSignalR();
5356

5457
IDragonFlyBuilder builder = new DragonFlyBuilder(services);
5558
builder.Init(api =>
@@ -87,13 +90,29 @@ public static IApplicationBuilder UseDragonFly(this IApplicationBuilder builder,
8790

8891
end.PreAuthBuilders.Foreach(a => a(new DragonFlyApplicationBuilder(x)));
8992

90-
x.UseMiddleware<RequireAuthentificationMiddleware>();
93+
//allows only requests from authenticated users
94+
x.Use(async (context, next) =>
95+
{
96+
if (context.User.Identity?.IsAuthenticated == false)
97+
{
98+
context.Response.StatusCode = StatusCodes.Status403Forbidden;
99+
}
100+
else
101+
{
102+
PermissionState.Enable();
103+
PermissionState.SetPrincipal(context.User);
104+
105+
await next(context);
106+
}
107+
});
91108

92109
end.Builders.Foreach(a => a(new DragonFlyApplicationBuilder(x)));
93110

94111
x.UseEndpoints(e =>
95112
{
96113
end.EndpointList.Foreach(a => a(new DragonFlyEndpointBuilder(e)));
114+
115+
e.MapHub<BackgroundTaskHub>("/taskhub");
97116
});
98117
}
99118
);

0 commit comments

Comments
 (0)