.NET MVC: ограничить действие на основе уровня роли - PullRequest
4 голосов
/ 20 сентября 2019

Я хотел бы узнать, есть ли лучший способ сделать это: предположим, у меня есть пользователи с ролями "SuperUser" , Admin , "Менеджер" , «Зарегистрировано» .

Все пользователи, зарегистрированные на сайте, имеют роль «Зарегистрировано» (например, пользователи «Менеджер» также имеют роль «Зарегистрировано»).

Теперь мне нужно управлять действием Удалить на контроллере пользователей.Чего я хочу достичь:

  • «Зарегистрированные» пользователи могут удалять только себя (например, удаление из регистрации на сайте)
  • «Администратор» может удалять себя и «Зарегистрированные» пользователи, ошибка не может удалить "SuperUser" и "Admin"
  • Пользователи "Admin" могут удалить себя, "Администраторы" и "Зарегистрированные" пользователи, ошибка не может удалить "SuperUser "
  • Пользователи" SuperUser "могут удалять все пользовательские роли, даже суперпользователи.

Поэтому я начал со следующего кода:

        [Authorize(Roles="Registered")]
        public void Delete(int id)
        {
            string[] AllowedRoles = { "SuperAdmin", "Manager" };

            if (_identity.FindFirst(ClaimTypes.UserData).Value == id.ToString())
            {
                //USER can delete himself!
                //TO DO: Deletion code
            }

            else if (User.IsInAnyRole(AllowedRoles))
            {
                //CHECK IF I CAN DELETE THE GIVEN USER
            }

        }

What I 'я собираюсь проверить каждую роль текущего пользователя с пользователем, чтобы удалить, но мне действительно не нравится писать много "если" .... Есть способ сделать это лучше?

Спасибо!

PS: не беспокойтесь о User.IsInAnyRole (это пользовательская функция, которая проверяет, принадлежит ли пользователь одной из указанных ролей.

Ответы [ 2 ]

1 голос
/ 22 сентября 2019

Интересно, есть ли аутентифицированные пользователи, которые не "зарегистрированы"?ИМО эта роль не нужна.Если вы не согласны, вы можете адаптировать приведенный ниже код.

Я не уверен, что _identity и User есть в вашем коде, но я предполагаю, что _identity является хранилищем usermanager и User - текущий пользователь httpcontext.Я предполагаю, что вам нужен UserManager, так как вы не сможете выполнить этот тест без доступа к сохраненным заявкам (как в AspNetUserClaims ).

Обратите внимание, что я не полностьюпротестируйте этот код.

// using System.Collections.Generic;
// using System.Linq;
// using System.Security.Claims;

// This method is available for all authenticated users
[Authorize]
public void Delete(int id)
{
    // Test if current user wants to delete itself
    if (User.FindFirst(ClaimTypes.UserData).Value != id.ToString())
    {
        // Find all roles of the current user.
        var roles = User.FindAll("role").Select(r => r.Value).ToList();

        // A fixed list, ordered by importance
        var allowedRoles = new List<string> { "SuperAdmin", "Admin", "Manager" };
        // Highest role of the current user
        var role = allowedRoles.Intersect(roles).FirstOrDefault();

        // "Registered" user is not allowed to do anything with other users
        if (role == null)
            return;

        // Get the rolename(s) of the target user. Something like this, where
        // _identity is a repository (usermanager?) that has access to the database
        var targetUserRoles = _identity.Where(u => u.Id == id).Roles().Select(r => r.Name).ToList();
        //var targetUserRoles = new List<string> { "Admin" };

        // Highest role of the target user, because you don't want to delete
        // a user that is both Manager and SuperAdmin when you are Admin.
        var targetUserRole = allowedRoles.Intersect(targetUserRoles).FirstOrDefault();
        // Users without a matching role may be deleted
        if (targetUserRole != null)
        {
            // Determine the importance of the role of both
            // the current user and the target user
            var targetIndex = allowedRoles.IndexOf(targetUserRole);
            var index = allowedRoles.IndexOf(role);

            // Index==0 is SuperAdmin
            // Otherwise index of role of targetuser must be higher
            if (index > 0 && targetIndex <= index)
                return;
        }
    }

    // If we got here we can safely delete the user.

    //TO DO: Deletion code
}

Если вы хотите расширить иерархию, вы можете просто добавить значение заявки в коллекцию allowRoles в соответствующей позиции.

1 голос
/ 20 сентября 2019

Сценарий, подобный тому, что я сделал, вместо того, чтобы иметь роли в виде строк, а затем перечислять флаги

[Flags]
public enum Permissions
{
    None = 0,
    Registered = 1 << 0,
    SuperAdmin = 1 << 1,
    Manager = 1 << 2,
    // Etc...
}

Используя этот метод, вы можете затем использовать этот метод для пользователя, чтобы определить, какие роли разрешены:

public bool IsInRole(Permissions roles)
{
    var rolesToCheck = roles.GetFlags().Where(p => p != Permissions.None);
    return rolesToCheck.Any(role => Roles.HasFlag(role));
}

и:

if(User.IsInRole(Permissions.SuperAdmin | Permissions.Manager)
{
    // Do something
}

Аналогичным образом вы также можете добавить это к атрибуту, создав собственное расширение атрибута Authorize:

public class CustomAuthorize : ActionFilterAttribute, IActionFilter
{
    public Permissions Roles { get; set; }

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        bool authorized = false;
        var roleFlags = Roles.GetFlags();
        if (!roleFlags.All(r => r == Permissions.None))
        {
            foreach (var role in roleFlags.Where(p => p != RolePermissions.None))
            {
                if (maritimeUser.Roles.HasFlag(role))
                {
                    authorized = true;
                }
            }
        }

    if (Roles == Permissions.None)
    {
        // No roles set, so authorise = okay
        return;
    }

    if (!authorized)
    {
        filterContext.Result =
            new RedirectToRouteResult(
                new RouteValueDictionary
                {
                    {"controller", "Account"},
                    {"action", "Unauthorised"},
                    {"area", "" }
                });
        return;
    }
}

и использовать

[CustomAuthorize(Roles = Permissions.SuperAdmin | Permissions.Manager)]
...