Skip to content

Commit d87c5a2

Browse files
committed
Renamed Requires to Require / Merged projection into server.
1 parent e6d0ceb commit d87c5a2

28 files changed

Lines changed: 538 additions & 120 deletions

.package/package.msbuild

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8-
<ProjectReference Include="../Shuttle.Access.Projection/Shuttle.Access.Projection.csproj"/>
98
<ProjectReference Include="../Shuttle.Access.Server/Shuttle.Access.Server.csproj"/>
109
<ProjectReference Include="../Shuttle.Access.WebApi/Shuttle.Access.WebApi.csproj"/>
1110
<DatabaseReference Include="../Shuttle.Access.Database/Shuttle.Access.Database.sqlproj" />

.package/webapi-appsettings.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@
3232
},
3333
"Shuttle": {
3434
"Access": {
35-
"OAuthRedirectUri": "http://localhost:3000/oauth",
36-
"OAuthProviderNames": [
37-
"GitHub"
35+
"KnownApplications": [
36+
{
37+
"Name": "Recall",
38+
"Title": "Shuttle.Recall.WebApi",
39+
"Description": "Provides endpoints that allow retrieving primitive events from a Sql Server event store.",
40+
"SessionTokenExchangeUrl": "http://localhost:3001/session/"
41+
}
3842
]
3943
},
4044
"OAuth": {

Shuttle.Access.AspNetCore/HttpContextExtensions.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Microsoft.AspNetCore.Http;
1+
using System.Security.Claims;
2+
using Microsoft.AspNetCore.Http;
23
using Microsoft.AspNetCore.Mvc;
34
using Shuttle.Core.Contract;
45
using System.Text.RegularExpressions;
@@ -8,6 +9,7 @@ namespace Shuttle.Access.AspNetCore;
89
public static class HttpContextExtensions
910
{
1011
private static readonly Regex TokenExpression = new(@"token\s*=\s*(?<token>[0-9a-fA-F-]{36})", RegexOptions.IgnoreCase);
12+
public const string SessionTokenClaimType = "http://shuttle.org/claims/session/token";
1113

1214
public static SessionTokenResult GetAccessSessionToken(this HttpContext context)
1315
{
@@ -43,4 +45,26 @@ public static SessionTokenResult GetAccessSessionToken(this HttpContext context)
4345
throw new("Could not retrieve the session token.");
4446
}
4547
}
48+
49+
public static SessionTokenResult GetPrincipalAccessSessionToken(this HttpContext context)
50+
{
51+
Guard.AgainstNull(context);
52+
53+
var sessionToken = context.User.Claims.FirstOrDefault(claim => claim.Type == SessionTokenClaimType)?.Value;
54+
55+
return sessionToken == null || !Guid.TryParse(sessionToken, out var token)
56+
? SessionTokenResult.Failure(new UnauthorizedResult())
57+
: SessionTokenResult.Success(token);
58+
}
59+
60+
public static void SetPrincipalAccessSessionToken(this HttpContext context, Guid sessionToken)
61+
{
62+
Guard.AgainstNull(context);
63+
64+
var identity = new ClaimsIdentity(context.User.Identity);
65+
66+
identity.AddClaim(new(SessionTokenClaimType, sessionToken.ToString()));
67+
68+
context.User = new(identity);
69+
}
4670
}

Shuttle.Access.AspNetCore/RequiresPermissionAttribute.cs renamed to Shuttle.Access.AspNetCore/RequirePermissionAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Shuttle.Access.AspNetCore;
66

7-
public class RequiresPermissionAttribute : TypeFilterAttribute
7+
public class RequirePermissionAttribute : TypeFilterAttribute
88
{
9-
public RequiresPermissionAttribute(string permission) : base(typeof(RequiresPermission))
9+
public RequirePermissionAttribute(string permission) : base(typeof(RequiresPermission))
1010
{
1111
Arguments = [permission];
1212
}

Shuttle.Access.AspNetCore/RequiresSessionAttribute.cs renamed to Shuttle.Access.AspNetCore/RequireSessionAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
namespace Shuttle.Access.AspNetCore;
66

7-
public class RequiresSessionAttribute : TypeFilterAttribute
7+
public class RequireSessionAttribute : TypeFilterAttribute
88
{
9-
public RequiresSessionAttribute() : base(typeof(RequiresSession))
9+
public RequireSessionAttribute() : base(typeof(RequiresSession))
1010
{
1111
Arguments = [];
1212
}

Shuttle.Access.AspNetCore/RouteHandlerBuilderExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ namespace Shuttle.Access.AspNetCore;
55

66
public static class RouteHandlerBuilderExtensions
77
{
8-
public static RouteHandlerBuilder RequiresPermission(this RouteHandlerBuilder builder, string permission)
8+
public static RouteHandlerBuilder RequirePermission(this RouteHandlerBuilder builder, string permission)
99
{
1010
return Guard.AgainstNull(builder, nameof(builder)).WithMetadata(new AccessPermissionRequirement(Guard.AgainstNullOrEmptyString(permission, nameof(permission))));
1111
}
1212

13-
public static RouteHandlerBuilder RequiresSession(this RouteHandlerBuilder builder)
13+
public static RouteHandlerBuilder RequireSession(this RouteHandlerBuilder builder)
1414
{
1515
return Guard.AgainstNull(builder, nameof(builder)).WithMetadata(new AccessSessionRequirement());
1616
}

Shuttle.Access.Server/HostApplication.cs

Lines changed: 0 additions & 41 deletions
This file was deleted.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.Options;
5+
using Shuttle.Access.Server.v1.MessageHandlers;
6+
using Shuttle.Core.Contract;
7+
using Shuttle.Core.Pipelines;
8+
using Shuttle.Esb;
9+
10+
namespace Shuttle.Access.Server;
11+
12+
public class KeepAliveObserver : IPipelineObserver<OnAfterGetMessage>
13+
{
14+
private readonly SemaphoreSlim _lock = new(1, 1);
15+
private readonly IServiceBus _serviceBus;
16+
private bool _keepAliveSent;
17+
private readonly ServerOptions _serverOptions;
18+
19+
public KeepAliveObserver(IOptions<ServerOptions> serverOptions, IServiceBus serviceBus)
20+
{
21+
_serverOptions = Guard.AgainstNull(Guard.AgainstNull(serverOptions).Value);
22+
_serviceBus = Guard.AgainstNull(serviceBus);
23+
}
24+
25+
public async Task ExecuteAsync(IPipelineContext<OnAfterGetMessage> pipelineContext)
26+
{
27+
await _lock.WaitAsync();
28+
29+
try
30+
{
31+
if (_keepAliveSent)
32+
{
33+
return;
34+
}
35+
36+
await _serviceBus.SendAsync(new MonitorKeepAlive(), builder => builder.Local().Defer(DateTime.UtcNow.Add(_serverOptions.MonitorKeepAliveInterval)));
37+
38+
_keepAliveSent = true;
39+
}
40+
finally
41+
{
42+
_lock.Release();
43+
}
44+
}
45+
46+
public async Task ResetAsync()
47+
{
48+
await _lock.WaitAsync();
49+
50+
try
51+
{
52+
_keepAliveSent = false;
53+
}
54+
finally
55+
{
56+
_lock.Release();
57+
}
58+
}
59+
}

Shuttle.Access.Server/Program.cs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
using Microsoft.Extensions.Hosting;
1212
using OpenTelemetry.Trace;
1313
using Serilog;
14+
using Shuttle.Access.Server.v1.EventHandlers;
1415
using Shuttle.Core.Contract;
1516
using Shuttle.Core.Data;
1617
using Shuttle.Core.DependencyInjection;
1718
using Shuttle.Core.Mediator;
19+
using Shuttle.Core.Pipelines;
1820
using Shuttle.Esb;
1921
using Shuttle.Esb.AzureStorageQueues;
2022
using Shuttle.Esb.Sql.Subscription;
2123
using Shuttle.Recall;
24+
using Shuttle.Recall.Logging;
25+
using Shuttle.Recall.Sql.EventProcessing;
2226
using Shuttle.Recall.Sql.Storage;
2327

2428
namespace Shuttle.Access.Server;
@@ -56,6 +60,7 @@ private static async Task Main(string[] args)
5660

5761
services
5862
.AddSingleton<IConfiguration>(configuration)
63+
.AddSingleton(configuration.GetSection(ServerOptions.SectionName).Get<ServerOptions>() ?? new ServerOptions())
5964
.AddLogging(builder =>
6065
{
6166
builder.AddSerilog();
@@ -83,7 +88,25 @@ private static async Task Main(string[] args)
8388

8489
builder.UseSqlServer();
8590
})
86-
.AddEventStore()
91+
.AddSqlEventProcessing(builder =>
92+
{
93+
builder.Options.ConnectionStringName = "Access";
94+
95+
builder.UseSqlServer();
96+
})
97+
.AddEventStore(builder =>
98+
{
99+
configuration.GetSection(EventStoreOptions.SectionName).Bind(builder.Options);
100+
101+
builder.AddProjection(ProjectionNames.Identity).AddEventHandler<IdentityHandler>();
102+
builder.AddProjection(ProjectionNames.Permission).AddEventHandler<PermissionHandler>();
103+
builder.AddProjection(ProjectionNames.Role).AddEventHandler<RoleHandler>();
104+
})
105+
.AddEventStoreLogging(builder =>
106+
{
107+
builder.Options.AddPipelineEventType<OnPipelineException>();
108+
builder.Options.AddPipelineEventType<OnAfterAcknowledgeEvent>();
109+
})
87110
.AddSqlSubscription(builder =>
88111
{
89112
builder.Options.ConnectionStringName = "Access";
@@ -97,32 +120,9 @@ private static async Task Main(string[] args)
97120
})
98121
.AddSingleton<IPasswordGenerator, DefaultPasswordGenerator>()
99122
.AddSingleton<IHashingService, HashingService>()
100-
.AddSingleton<IHostedService, ApplicationHostedService>()
123+
.AddSingleton<IHostedService, ServerHostedService>()
124+
.AddSingleton<KeepAliveObserver>()
101125
.AddSingleton(TracerProvider.Default.GetTracer("Shuttle.Access.Server"));
102-
103-
//services.AddOpenTelemetry()
104-
// .WithTracing(
105-
// builder => builder
106-
// .AddServiceBusInstrumentation(openTelemetryBuilder =>
107-
// {
108-
// configuration.GetSection(ServiceBusOpenTelemetryOptions.SectionName).Bind(openTelemetryBuilder.Options);
109-
// })
110-
// .AddMediatorInstrumentation(openTelemetryBuilder =>
111-
// {
112-
// configuration.GetSection(MediatorOpenTelemetryOptions.SectionName).Bind(openTelemetryBuilder.Options);
113-
// })
114-
// .AddRecallInstrumentation(openTelemetryBuilder =>
115-
// {
116-
// configuration.GetSection(RecallOpenTelemetryOptions.SectionName).Bind(openTelemetryBuilder.Options);
117-
// })
118-
// .AddSqlClientInstrumentation(options =>
119-
// {
120-
// options.SetDbStatementForText = true;
121-
// })
122-
// .AddJaegerExporter(options =>
123-
// {
124-
// options.AgentHost = Environment.GetEnvironmentVariable("JAEGER_AGENT_HOST");
125-
// }));
126126
})
127127
.Build();
128128

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.Hosting;
5+
using Microsoft.Extensions.Options;
6+
using Shuttle.Access.Messages.v1;
7+
using Shuttle.Core.Contract;
8+
using Shuttle.Core.Data;
9+
using Shuttle.Core.Mediator;
10+
using Shuttle.Core.Pipelines;
11+
using Shuttle.Esb;
12+
13+
namespace Shuttle.Access.Server;
14+
15+
public class ServerHostedService : IHostedService
16+
{
17+
private readonly IDatabaseContextFactory _databaseContextFactory;
18+
private readonly Type _inboxMessagePipeline = typeof(InboxMessagePipeline);
19+
private readonly IMediator _mediator;
20+
private readonly IPipelineFactory _pipelineFactory;
21+
private readonly ServerOptions _serverOptions;
22+
23+
public ServerHostedService(IOptions<ServerOptions> serverOptions, IHostApplicationLifetime hostApplicationLifetime, IDatabaseContextFactory databaseContextFactory, IMediator mediator, IPipelineFactory pipelineFactory)
24+
{
25+
Guard.AgainstNull(hostApplicationLifetime, nameof(hostApplicationLifetime)).ApplicationStarted.Register(OnStarted);
26+
27+
_serverOptions = Guard.AgainstNull(Guard.AgainstNull(serverOptions).Value);
28+
_databaseContextFactory = Guard.AgainstNull(databaseContextFactory);
29+
_mediator = Guard.AgainstNull(mediator);
30+
_pipelineFactory = Guard.AgainstNull(pipelineFactory);
31+
32+
_pipelineFactory.PipelineCreated += OnPipelineCreated;
33+
}
34+
35+
public async Task StartAsync(CancellationToken cancellationToken)
36+
{
37+
await Task.CompletedTask;
38+
}
39+
40+
public async Task StopAsync(CancellationToken cancellationToken)
41+
{
42+
_pipelineFactory.PipelineCreated -= OnPipelineCreated;
43+
44+
await Task.CompletedTask;
45+
}
46+
47+
private void OnPipelineCreated(object? sender, PipelineEventArgs e)
48+
{
49+
if (_serverOptions.MonitorKeepAliveInterval.Equals(TimeSpan.Zero))
50+
{
51+
return;
52+
}
53+
54+
var pipelineType = e.Pipeline.GetType();
55+
56+
if (pipelineType == _inboxMessagePipeline)
57+
{
58+
e.Pipeline.AddObserver<KeepAliveObserver>();
59+
}
60+
}
61+
62+
private void OnStarted()
63+
{
64+
using (_databaseContextFactory.Create())
65+
{
66+
_mediator.SendAsync(new ConfigureApplication()).GetAwaiter().GetResult();
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)