我试图限制 Umbraco 中的用户,以便具有“collarDatabase”角色的用户只能将成员添加到“Collar_Database”成员组。如果用户尝试将成员添加到任何其他组,则操作应该失败,并且不应保存任何更改。如果检测到未经授权的组,我将使用 MemberSavingNotification 取消保存。问题是处理程序会针对同一请求多次触发,并且即使在调用 notification.CancelOperation() 之后,成员更改仍然会保存。 UI 首先显示已保存的内容,然后显示三条用户未经授权的错误消息。如何确保处理程序每个请求仅运行一次,并且如果检测到未经授权的组,则完全阻止保存?
这是我的 handler.cs:
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Linq;
using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Security;
using Umbraco.Cms.Core.Services;
public class RestrictMemberGroupEditHandler : INotificationHandler<MemberSavingNotification>
{
private readonly ILogger<RestrictMemberGroupEditHandler> _logger;
private readonly IMemberService _memberService;
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
private static readonly ConcurrentDictionary<string, bool> ProcessedRequestIds = new ConcurrentDictionary<string, bool>();
public RestrictMemberGroupEditHandler(
ILogger<RestrictMemberGroupEditHandler> logger,
IMemberService memberService,
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
{
_logger = logger;
_memberService = memberService;
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
}
public void Handle(MemberSavingNotification notification)
{
// Get a unique identifier for the current request
string requestId = GetRequestId();
if (ProcessedRequestIds.ContainsKey(requestId))
{
_logger.LogInformation("Skipping execution for RequestId {RequestId} as it has already been processed.", requestId);
return;
}
ProcessedRequestIds.TryAdd(requestId, true);
var currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
if (currentUser == null)
{
_logger.LogWarning("No current backend user found.");
return;
}
_logger.LogInformation("RestrictMemberGroupEditHandler triggered for user: {UserName}", currentUser.Name);
if (!currentUser.Groups.Any(g => g.Alias == "collarDatabase"))
{
_logger.LogInformation("User {UserName} is not in the 'Collar_Database' role. No restriction applied.", currentUser.Name);
return;
}
foreach (var member in notification.SavedEntities)
{
var assignedGroups = _memberService.GetAllRoles(member.Username).ToList();
_logger.LogInformation("Groups being assigned to member {MemberName}: {Groups}",
member.Name,
string.Join(", ", assignedGroups));
// Check for unauthorized groups
var unauthorizedGroups = assignedGroups.Where(group => group != "Collar_Database").ToList();
if (unauthorizedGroups.Any())
{
_logger.LogWarning("User {UserName} attempted to add member {MemberName} to unauthorized groups: {Groups}",
currentUser.Name,
member.Name,
string.Join(", ", unauthorizedGroups));
notification.CancelOperation(new EventMessage(
"Access Denied",
$"You can only assign members to the 'Collar_Database' group. Unauthorized groups: {string.Join(", ", unauthorizedGroups)}",
EventMessageType.Error));
return;
}
}
ProcessedRequestIds.TryRemove(requestId, out _);
}
private string GetRequestId()
{
return System.Guid.NewGuid().ToString();
}
}
这是我的作曲家:
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Notifications;
public class RestrictMemberGroupEditComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.AddNotificationHandler<MemberSavingNotification, RestrictMemberGroupEditHandler>();
}
}
这是 John Smith 尝试将 John Doe 添加到未经授权的群组时的日志。 .
{"@t":"2024-12-09T01:04:51.369Z","@mt":"RestrictMemberGroupEditHandler triggered for user: {UserName}","UserName":"John Smith"}
{"@t":"2024-12-09T01:04:51.372Z","@mt":"Groups being assigned to member {MemberName}: {Groups}","MemberName":"John Doe","Groups":"example-group, Collar_Database"}
{"@t":"2024-12-09T01:04:51.372Z","@mt":"User {UserName} attempted to add member {MemberName} to unauthorized groups: {Groups}","UserName":"John Smith","MemberName":"John Doe","Groups":"example-group"}
{"@t":"2024-12-09T01:04:51.405Z","@mt":"RestrictMemberGroupEditHandler triggered for user: {UserName}","UserName":"John Smith"}
{"@t":"2024-12-09T01:04:51.406Z","@mt":"Groups being assigned to member {MemberName}: {Groups}","MemberName":"John Doe","Groups":"example-group, Collar_Database"}
{"@t":"2024-12-09T01:04:51.407Z","@mt":"User {UserName} attempted to add member {MemberName} to unauthorized groups: {Groups}","UserName":"John Smith","MemberName":"John Doe","Groups":"example-group"}
仅从外观上看,我可以在文档中看到,似乎在保存成员时会调用 MemberSavedNotification,因此您的代码将工作到很晚。 它说:
The MemberSavedNotification, which is triggered whenever a Member is saved.
This notification is triggered when any Member is saved.
但是同一个会员通知里还有另外一个,你也许可以看一下,它叫:
会员保存通知
会员保存应该在实际保存之前关闭。
MemberSavingNotification(IEnumerable<IMember>, EventMessages)
Initializes a new instance of the MemberSavingNotification.
Declaration
public MemberSavingNotification(IEnumerable<IMember> target, EventMessages messages)
Parameters
Type Name Description
IEnumerable<IMember> target
Gets the collection of IMember objects being saved.
EventMessages messages
Initializes a new instance of the EventMessages.
如果不起作用,您可以将其保存,然后将其删除。但这不是一个很好的解决方案。