Skip to content

Commit 67abecc

Browse files
authored
Add defensive coding to the member application initializer (#19760)
1 parent 417f151 commit 67abecc

File tree

1 file changed

+48
-21
lines changed

1 file changed

+48
-21
lines changed

src/Umbraco.Cms.Api.Delivery/Handlers/InitializeMemberApplicationNotificationHandler.cs

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Umbraco.Cms.Core.Events;
77
using Umbraco.Cms.Core.Notifications;
88
using Umbraco.Cms.Core.Services;
9+
using Umbraco.Cms.Core.Sync;
910
using Umbraco.Cms.Infrastructure.Security;
1011

1112
namespace Umbraco.Cms.Api.Delivery.Handlers;
@@ -16,16 +17,21 @@ internal sealed class InitializeMemberApplicationNotificationHandler : INotifica
1617
private readonly ILogger<InitializeMemberApplicationNotificationHandler> _logger;
1718
private readonly DeliveryApiSettings _deliveryApiSettings;
1819
private readonly IServiceScopeFactory _serviceScopeFactory;
20+
private readonly IServerRoleAccessor _serverRoleAccessor;
21+
private static readonly SemaphoreSlim _locker = new(1);
22+
private static bool _isInitialized = false;
1923

2024
public InitializeMemberApplicationNotificationHandler(
2125
IRuntimeState runtimeState,
2226
IOptions<DeliveryApiSettings> deliveryApiSettings,
2327
ILogger<InitializeMemberApplicationNotificationHandler> logger,
24-
IServiceScopeFactory serviceScopeFactory)
28+
IServiceScopeFactory serviceScopeFactory,
29+
IServerRoleAccessor serverRoleAccessor)
2530
{
2631
_runtimeState = runtimeState;
2732
_logger = logger;
2833
_serviceScopeFactory = serviceScopeFactory;
34+
_serverRoleAccessor = serverRoleAccessor;
2935
_deliveryApiSettings = deliveryApiSettings.Value;
3036
}
3137

@@ -36,34 +42,55 @@ public async Task HandleAsync(UmbracoApplicationStartingNotification notificatio
3642
return;
3743
}
3844

39-
// we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
40-
// install that is not allowed (no connection string means no DbContext)
41-
using IServiceScope scope = _serviceScopeFactory.CreateScope();
42-
IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService<IMemberApplicationManager>();
43-
44-
if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
45+
if (_serverRoleAccessor.CurrentServerRole is ServerRole.Subscriber)
4546
{
46-
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
47+
// subscriber instances should not alter the member application
4748
return;
4849
}
4950

50-
if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
51+
try
5152
{
52-
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
53-
return;
54-
}
53+
await _locker.WaitAsync(cancellationToken);
54+
if (_isInitialized)
55+
{
56+
return;
57+
}
58+
59+
_isInitialized = true;
60+
61+
// we cannot inject the IMemberApplicationManager because it ultimately takes a dependency on the DbContext ... and during
62+
// install that is not allowed (no connection string means no DbContext)
63+
using IServiceScope scope = _serviceScopeFactory.CreateScope();
64+
IMemberApplicationManager memberApplicationManager = scope.ServiceProvider.GetRequiredService<IMemberApplicationManager>();
65+
66+
if (_deliveryApiSettings.MemberAuthorization?.AuthorizationCodeFlow?.Enabled is not true)
67+
{
68+
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
69+
return;
70+
}
5571

56-
if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
57-
&& ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
72+
if (ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls) is false)
73+
{
74+
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
75+
return;
76+
}
77+
78+
if (_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls.Any()
79+
&& ValidateRedirectUrls(_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls) is false)
80+
{
81+
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
82+
return;
83+
}
84+
85+
await memberApplicationManager.EnsureMemberApplicationAsync(
86+
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
87+
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
88+
cancellationToken);
89+
}
90+
finally
5891
{
59-
await memberApplicationManager.DeleteMemberApplicationAsync(cancellationToken);
60-
return;
92+
_locker.Release();
6193
}
62-
63-
await memberApplicationManager.EnsureMemberApplicationAsync(
64-
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LoginRedirectUrls,
65-
_deliveryApiSettings.MemberAuthorization.AuthorizationCodeFlow.LogoutRedirectUrls,
66-
cancellationToken);
6794
}
6895

6996
private bool ValidateRedirectUrls(Uri[] redirectUrls)

0 commit comments

Comments
 (0)