Пользовательский CodeAccessSecurityAttribute, который принимает много ролей - PullRequest
2 голосов
/ 02 декабря 2011

Я работаю над некоторой защитой на основе ролей для нашего приложения, и я по сути хочу сделать настраиваемую версию MVC AuthorizeAttribute - но только на уровне бизнес-логики, где мы не ссылаемся на MVC.

Я посмотрел на PrincipalPermissionAttribute, но, похоже, у него нет способа настроить его, поскольку он запечатан.Я просто хочу создать собственную версию, в которой я могу проверять членство в any списка ролей без использования нескольких атрибутов, а также определять, где искать членство в роли.

Есть ли что-то подобное в .Net, что мне не хватает?Или у кого-нибудь есть представление о том, как это сделать без переопределения AuthorizeAttribute / RoleProvider / etc в ASP.Net?

РЕДАКТИРОВАТЬ

В настоящее время у меня работает императивная версия, ноЯ предпочел бы иметь версию декларативного атрибута, так как ее легче увидеть над методом / классом.

Сейчас у меня есть следующее в абстрактном базовом классе для моего бизнес-уровня:

protected void EnsureEditorLevelAccess()
{
    var allowedRoles = new[]
                            {
                                Roles.Administrator,
                                Roles.Editor,
                            };

    var roles = GetAccountRoles(GetCurrentUsername());

    if (roles.Any(role => allowedRoles.Contains(role)))
    {
        return;
    }

    throw new SecurityException("You do not have sufficient privileges for this operation.");
}

Мне нравится возможность использовать Roles.Administrator и т. Д., Потому что имена ролей отвратительны (основаны на группах Active Directory ...), поэтому я подумал о том, чтобы обернуть эти детали в конструктор пользовательского атрибута, который я могу простошлепнуть поверх классов / методов.

GetAccountRoles - это просто фасад над вводимым свойством поставщика ролей, который я могу установить для использования либо AD, либо тестовой версии, использующей базу данных.

Я мог бы создать подкласс Attribute, но не уверен, как это сработает с проверкой безопасности.

Ответы [ 2 ]

5 голосов
/ 02 декабря 2011

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


После обновления вопроса ...

На самом деле возможно использовать «любую» логику с PrincipalPermission, хотя это требует объединения нескольких экземпляров, что не очень удобно для работы с атрибутом. Это делает более разумным создание пользовательского атрибута, который может выглядеть примерно так:

[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class AnyRolePermissionAttribute : CodeAccessSecurityAttribute
{
    public AnyRolePermissionAttribute(SecurityAction action)
        : base(action)
    {
    }

    public string Roles { get; set; }

    public override IPermission CreatePermission()
    {
        IList<string> roles = (this.Roles ?? string.Empty).Split(',', ';')
                                .Select(s => s.Trim())
                                .Where(s => s.Length > 0)
                                .Distinct()
                                .ToList();

        IPermission result;
        if (roles.Count == 0)
        {
            result = new PrincipalPermission(null, null, true);
        }
        else
        {
            result = new PrincipalPermission(null, roles[0]);
            for (int i = 1; i < roles.Count; i++)
            {
                result = result.Union(new PrincipalPermission(null, roles[i]));
            }
        }

        return result;
    }
}

К сожалению, вы не можете использовать массивы в атрибутах безопасности, поэтому список ролей должен быть представлен в виде строки. e.g.:

[AnyRolePermission(SecurityAction.Demand, Roles = "Foo, Bar")]

Вы можете использовать его со своими константами через конкатенацию времени разработки. e.g.:

[AnyRolePermission(SecurityAction.Demand, Roles = Roles.Administrator + ", " + Roles.Editor)]

Что касается вашего настраиваемого поставщика ролей, подходящее место для его использования - в субъекте потока, а не в разрешении или атрибуте. Например, если вы в настоящее время используете GenericPrincipal, вы можете заменить его на пользовательский принципал, который использует ваш пользовательский поставщик ролей для получения ролей целевого удостоверения.

0 голосов
/ 02 декабря 2011

Вы можете получить свой собственный CodeAccessSecurityAttribute и реализовать свою логику вокруг Thread.CurrentPrincipal (http://msdn.microsoft.com/en-us/library/system.security.permissions.codeaccesssecurityattribute.aspx).

, по сути, вы хотите проверить allowedRoles.Any(r => Thread.CurrentPrincipal.IsInRole(r))

...