如何将用户限制在 Umbraco 中的特定成员组并防止未经授权的保存?

问题描述 投票:0回答:1

我试图限制 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"}
c# umbraco
1个回答
0
投票

仅从外观上看,我可以在文档中看到,似乎在保存成员时会调用 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.

如果不起作用,您可以将其保存,然后将其删除。但这不是一个很好的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.