Skip to content

Commit dd5dce4

Browse files
committedOct 13, 2024
Session refresh implemented.
1 parent dac6f8a commit dd5dce4

File tree

7 files changed

+225
-5
lines changed

7 files changed

+225
-5
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
3+
namespace Shuttle.Access.Application;
4+
5+
public class RefreshSession
6+
{
7+
public string IdentityName { get; }
8+
public Guid? Token { get; }
9+
10+
public RefreshSession(Guid token)
11+
{
12+
Token = token;
13+
}
14+
15+
public RefreshSession(string identityName)
16+
{
17+
IdentityName = identityName;
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.Threading.Tasks;
2+
using Shuttle.Access.Messages.v1;
3+
using Shuttle.Core.Contract;
4+
using Shuttle.Core.Mediator;
5+
using Shuttle.Esb;
6+
7+
namespace Shuttle.Access.Application;
8+
9+
public class RefreshSessionParticipant : IAsyncParticipant<RefreshSession>
10+
{
11+
private readonly IAccessService _accessService;
12+
private readonly IAuthorizationService _authorizationService;
13+
private readonly IServiceBus _serviceBus;
14+
private readonly ISessionRepository _sessionRepository;
15+
16+
public RefreshSessionParticipant(IServiceBus serviceBus, IAccessService accessService, IAuthorizationService authorizationService, ISessionRepository sessionRepository)
17+
{
18+
_serviceBus = Guard.AgainstNull(serviceBus);
19+
_accessService = Guard.AgainstNull(accessService);
20+
_authorizationService = Guard.AgainstNull(authorizationService);
21+
_sessionRepository = Guard.AgainstNull(sessionRepository);
22+
}
23+
24+
public async Task ProcessMessageAsync(IParticipantContext<RefreshSession> context)
25+
{
26+
Guard.AgainstNull(context);
27+
28+
var session = context.Message.Token.HasValue
29+
? await _sessionRepository.FindAsync(context.Message.Token.Value)
30+
: await _sessionRepository.FindAsync(context.Message.IdentityName);
31+
32+
if (session == null)
33+
{
34+
return;
35+
}
36+
37+
session.ClearPermissions();
38+
39+
foreach (var permission in await _authorizationService.GetPermissionsAsync(session.IdentityName))
40+
{
41+
session.AddPermission(permission);
42+
}
43+
44+
await _sessionRepository.SaveAsync(session);
45+
46+
_accessService.Flush(session.Token);
47+
48+
await _serviceBus.PublishAsync(new SessionRefreshed
49+
{
50+
Token = session.Token
51+
});
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System;
2+
3+
namespace Shuttle.Access.Messages.v1;
4+
5+
public class SessionRefreshed
6+
{
7+
public Guid Token { get; set; }
8+
}

‎Shuttle.Access.Server/Program.cs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ private static async Task Main(string[] args)
7777
})
7878
.AddEventStore()
7979
.AddSqlSubscription()
80+
.AddDataStoreAccessService()
8081
.AddMediator(builder =>
8182
{
8283
builder.AddParticipants(Assembly.Load("Shuttle.Access.Application"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using Shuttle.Access.Application;
2+
using Shuttle.Access.DataAccess;
3+
using Shuttle.Access.Messages.v1;
4+
using Shuttle.Core.Contract;
5+
using Shuttle.Core.Data;
6+
using Shuttle.Core.Mediator;
7+
using Shuttle.Esb;
8+
using Shuttle.Recall;
9+
10+
namespace Shuttle.Access.WebApi.Handlers;
11+
12+
public class AccessServiceHandler :
13+
IAsyncMessageHandler<IdentityRoleSet>,
14+
IAsyncMessageHandler<RolePermissionSet>,
15+
IAsyncMessageHandler<PermissionStatusSet>
16+
{
17+
private readonly IMediator _mediator;
18+
private readonly IDatabaseContextFactory _databaseContextFactory;
19+
private readonly IIdentityQuery _identityQuery;
20+
private readonly IPermissionQuery _permissionQuery;
21+
private readonly IProjectionRepository _projectionRepository;
22+
private readonly ISessionQuery _sessionQuery;
23+
24+
public AccessServiceHandler(IDatabaseContextFactory databaseContextFactory, IIdentityQuery identityQuery, IProjectionRepository projectionRepository, IPermissionQuery permissionQuery, ISessionQuery sessionQuery, IMediator mediator)
25+
{
26+
_databaseContextFactory = Guard.AgainstNull(databaseContextFactory);
27+
_identityQuery = Guard.AgainstNull(identityQuery);
28+
_projectionRepository = Guard.AgainstNull(projectionRepository);
29+
_permissionQuery = Guard.AgainstNull(permissionQuery);
30+
_sessionQuery = Guard.AgainstNull(sessionQuery);
31+
_mediator = Guard.AgainstNull(mediator);
32+
}
33+
34+
public async Task ProcessMessageAsync(IHandlerContext<IdentityRoleSet> context)
35+
{
36+
Guard.AgainstNull(context, nameof(context));
37+
38+
var message = context.Message;
39+
40+
using (new DatabaseContextScope())
41+
await using (_databaseContextFactory.Create())
42+
{
43+
if (await _projectionRepository.GetSequenceNumberAsync(ProjectionNames.Identity) < message.SequenceNumber)
44+
{
45+
await context.SendAsync(message, c => c.Defer(DateTime.UtcNow.AddSeconds(5)).Local());
46+
47+
return;
48+
}
49+
50+
if (message.Active)
51+
{
52+
await RefreshAsync(new DataAccess.Query.Identity.Specification().WithRoleId(message.RoleId));
53+
}
54+
else
55+
{
56+
await RefreshAsync(new DataAccess.Query.Session.Specification().AddPermissions(
57+
(await _permissionQuery.SearchAsync(new DataAccess.Query.Permission.Specification().AddRoleId(message.RoleId)))
58+
.Select(item => item.Name)));
59+
}
60+
}
61+
}
62+
63+
public async Task ProcessMessageAsync(IHandlerContext<PermissionStatusSet> context)
64+
{
65+
Guard.AgainstNull(context, nameof(context));
66+
67+
var message = context.Message;
68+
69+
using (new DatabaseContextScope())
70+
await using (_databaseContextFactory.Create())
71+
{
72+
if (await _projectionRepository.GetSequenceNumberAsync(ProjectionNames.Permission) < message.SequenceNumber)
73+
{
74+
await context.SendAsync(message, c => c.Defer(DateTime.UtcNow.AddSeconds(5)).Local());
75+
76+
return;
77+
}
78+
79+
if (message.Status == (int)PermissionStatus.Removed)
80+
{
81+
await RefreshAsync(new DataAccess.Query.Session.Specification().AddPermission(message.Name));
82+
}
83+
else
84+
{
85+
await RefreshAsync(new DataAccess.Query.Identity.Specification().WithPermissionId(message.Id));
86+
}
87+
}
88+
}
89+
90+
public async Task ProcessMessageAsync(IHandlerContext<RolePermissionSet> context)
91+
{
92+
Guard.AgainstNull(context, nameof(context));
93+
94+
var message = context.Message;
95+
96+
using (new DatabaseContextScope())
97+
await using (_databaseContextFactory.Create())
98+
{
99+
if (await _projectionRepository.GetSequenceNumberAsync(ProjectionNames.Role) < message.SequenceNumber)
100+
{
101+
await context.SendAsync(message, c => c.Defer(DateTime.UtcNow.AddSeconds(5)).Local());
102+
103+
return;
104+
}
105+
106+
if (message.Active)
107+
{
108+
await RefreshAsync(new DataAccess.Query.Identity.Specification().WithRoleId(message.RoleId));
109+
}
110+
else
111+
{
112+
await RefreshAsync(new DataAccess.Query.Session.Specification().AddPermissions(
113+
(await _permissionQuery.SearchAsync(new DataAccess.Query.Permission.Specification().AddId(message.PermissionId)))
114+
.Select(item => item.Name)));
115+
}
116+
}
117+
}
118+
119+
private async Task RefreshAsync(DataAccess.Query.Session.Specification specification)
120+
{
121+
foreach (var session in await _sessionQuery.SearchAsync(specification))
122+
{
123+
await _mediator.SendAsync(new RefreshSession(session.Token));
124+
}
125+
}
126+
127+
private async Task RefreshAsync(DataAccess.Query.Identity.Specification specification)
128+
{
129+
foreach (var identity in await _identityQuery.SearchAsync(specification))
130+
{
131+
await _mediator.SendAsync(new RefreshSession(identity.Name));
132+
}
133+
}
134+
}

‎Shuttle.Access.WebApi/Program.cs

+8-3
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ public static void Main(string[] args)
109109
{
110110
webApplicationBuilder.Configuration.GetSection(ServiceBusOptions.SectionName).Bind(builder.Options);
111111

112+
builder.Options.Asynchronous = true;
112113
builder.Options.Subscription.ConnectionStringName = "Access";
113114

114115
builder.AddSubscription<IdentityRoleSet>();
@@ -117,10 +118,14 @@ public static void Main(string[] args)
117118
})
118119
.AddAzureStorageQueues(builder =>
119120
{
120-
builder.AddOptions("azure", new()
121+
var queueOptions = webApplicationBuilder.Configuration.GetSection($"{AzureStorageQueueOptions.SectionName}:Access").Get<AzureStorageQueueOptions>() ?? new();
122+
123+
if (string.IsNullOrWhiteSpace(queueOptions.StorageAccount))
121124
{
122-
ConnectionString = webApplicationBuilder.Configuration.GetConnectionString("azure")
123-
});
125+
queueOptions.ConnectionString = webApplicationBuilder.Configuration.GetConnectionString("azure") ?? string.Empty;
126+
}
127+
128+
builder.AddOptions("azure", queueOptions);
124129
})
125130
.AddEventStore()
126131
.AddSqlEventStorage(builder =>

‎Shuttle.Access.WebApi/appsettings.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@
1414
"OAuthRedirectUri": "http://localhost:3000/oauth",
1515
"OAuthProviderNames": [
1616
"GitHub"
17-
]
17+
]
1818
},
1919
"OAuth": {
2020
"GitHub": {
2121
"authorizationUrl": "https://github.com/login/oauth/authorize?client_id=__ClientId__&redirect_uri=__RedirectUri__&scope=__Scope__",
2222
"scope": "user:email",
2323
"tokenUrl": "https://github.com/login/oauth/access_token",
2424
"dataUrl": "https://api.github.com/user"
25-
}
25+
}
2626
},
2727
"ServiceBus": {
2828
"Inbox": {

0 commit comments

Comments
 (0)
Please sign in to comment.