Сокращение пользовательского интерфейса MVC в зависимости от атрибута авторизации контроллера / действия - PullRequest
2 голосов
/ 08 ноября 2011

У меня есть защищенное приложение с атрибутом Authorize для каждого действия.

[Authorize(Roles = "Role1,Role2")]
public ActionResult MyAction(int id)
{
    return View();
}

В моем пользовательском интерфейсе есть ссылки на эти контроллеры / действия. Я хотел бы создать собственный HtmlHelper для ссылок, который принимает имена контроллеров и действий:

@Html.SecuredLink("Click Me", "MyAction", "MyController");

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

public static MvcHtmlString SecuredLink(this HtmlHelper helper, string text, string action, string controller)
{        
    var userId = Membership.GetUserId();

    var userHasRightsToThisAction = IsActionAccessibleToUser(helper.ViewContext.RequestContext.HttpContext, controller, action); // <- How would this work?

    if (userHasRightsToThisAction )
    {
       // Render Link
       // ...
    }
}

Мне не удалось найти способ легко протестировать действие из кода на предмет статуса авторизации.

Ответы [ 2 ]

3 голосов
/ 10 ноября 2011

ОК нашел решение. Покопавшись в MvcSiteMap, который, как я знаю, выполняет подстройку безопасности, я обнаружил следующую статью об этом:

http://blog.maartenballiauw.be/post/2008/08/29/Building-an-ASPNET-MVC-sitemap-provider-with-security-trimming.aspx

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

    /// <summary>
    /// Determine if a controller/action is accessible for a user
    /// </summary>
    /// <param name="context">Current HttpContext</param>
    /// <param name="controllerName">Target controller</param>
    /// <param name="actionName">Target action</param>
    /// <returns>True/false if the action is accessible</returns>
    public static bool IsActionAccessibleToUser(HttpContextBase context, string controllerName, string actionName)
    {
        // Find current handler
        MvcHandler handler = context.Handler as MvcHandler;

        if (handler != null)
        {
            // try to figure out the controller class
            IController controller = null;
            try
            {
                controller = ControllerBuilder.Current.GetControllerFactory().CreateController(handler.RequestContext, controllerName);                    
            }
            catch (System.Web.HttpException e)
            {
                throw new Exception("The controller '" + controllerName + "Controller' was not found.", e);
            }

            // Find all AuthorizeAttributes on the controller class and action method
            object[] controllerAttributes = controller.GetType().GetCustomAttributes(typeof(AuthorizeAttribute), true);
            object[] actionAttributes = controller.GetType().GetMethod(actionName).GetCustomAttributes(typeof(AuthorizeAttribute), true);

            // No attributes, then the action is open to all
            if (controllerAttributes.Length == 0 && actionAttributes.Length == 0) return true;

            // Find out current principal
            IPrincipal principal = handler.RequestContext.HttpContext.User;

            // Do we pass the roles for the controller?
            string roles = "";
            if (controllerAttributes.Length > 0)
            {
                AuthorizeAttribute attribute = controllerAttributes[0] as AuthorizeAttribute;
                roles = attribute.Roles;

                if (!PassRoleValidation(principal, roles)) return false;
            }

            // Do we pass the roles for the action?
            if (actionAttributes.Length > 0)
            {
                AuthorizeAttribute attribute = actionAttributes[0] as AuthorizeAttribute;
                roles = attribute.Roles;

                if (!PassRoleValidation(principal, roles)) return false;
            }

            return true;
        }

        return false;
    }

    private static bool PassRoleValidation(IPrincipal principal, string roles)
    {
        // no roles, then all we need to be is authenticated
        if (string.IsNullOrEmpty(roles) && principal.Identity.IsAuthenticated) return true;

        string[] roleArray = roles.Split(',');

        // if role contains "*", it's open to all
        if (roleArray.Any(role => role == "*")) return true;

        // Determine if the current user is allowed to access the current node
        if (roleArray.Any(principal.IsInRole)) return true;

        return false;
    }
0 голосов
/ 09 ноября 2011

Хорошо, быстрое и грязное решение:

подготовить функцию для построения серверной части Urls

что-то вроде этого, вероятно, будет лучшим выбором:

public static string GetUrl(string Action, string Controller, object RouteValues) {
    UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext);
    return Url.Action(Action, Controller, RouteValues);
}

В помощнике получите информацию об аутентификации пользователя и верните встроенный URL или строку. Пусто.

public static string SecureLink(this HtmlHelper helper, string Action, string Controller, object RouteValues)
{
  YourUserObject LoggedUser = /* Whatever you need to obtain your UserId */
  if (LoggedUser.IsSuperUser) {
    return GetUrl(Action, Controller, RouteValues);
  }
  return string.empty;
}

Если ваш результат закодирован в HTML, просто используйте MvcHtmlString вместо строки в качестве возвращаемого значения. В противном случае остерегайтесь, вам может понадобиться использовать @Html.Raw для его излучения.

PS: очевидно, что я не добавил полную генерацию <a href .../> в этом примере кода, вы сами решаете, какие параметры нужны (добавьте их в сигнатуру Helper), я обычно копирую сигнатуры других @Html помощников (поэтому имя, значение и список атрибутов Html).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...