Link do akcji "Security aware"?
Jak utworzyć łącze akcji "Security aware", które wykrywa, czy użytkownik jest upoważniony do kliknięcia (wywołania) akcji?
Ukryj link, jeśli użytkownik nie może korzystać z tej akcji...
W zależności od
- www.config (authorization) oraz
- [Autoryzuj] atrybuty działań
PS
Myślę, że to zła praktyka mieszać te 2 w MVC?
3 answers
To jest jakiś kod pobrany z projektu MvcSitemap i zmodyfikowany na mój własny użytek. Jeśli dobrze pamiętam ten kod został zmodyfikowany dla MVC2 i niektóre funkcje mogą być z powrotem przeniesione do MVC1.
Jego nie złe praktyki w ogóle mieszać MVC i FormsAuthentication razem, domyślne metody uwierzytelniania MVC są zbudowane wokół istniejących Asp.net Infrastruktura bezpieczeństwa.
Kod określający, czy użytkownik ma uprawnienia:
public static class SecurityTrimmingExtensions
{
public static bool HasActionPermission( this HtmlHelper htmlHelper, string actionName, string controllerName )
{
//if the controller name is empty the ASP.NET convention is:
//"we are linking to a different controller
ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName)
? htmlHelper.ViewContext.Controller
: GetControllerByName(htmlHelper, controllerName);
var controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);
var controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());
var actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
return ActionIsAuthorized(controllerContext, actionDescriptor);
}
private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false; // action does not exist so say yes - should we authorise this?!
AuthorizationContext authContext = new AuthorizationContext(controllerContext);
// run each auth filter until on fails
// performance could be improved by some caching
foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
{
authFilter.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
private static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName)
{
// Instantiate the controller and call Execute
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName);
if (controller == null)
{
throw new InvalidOperationException(
String.Format(
CultureInfo.CurrentUICulture,
"Controller factory {0} controller {1} returned null",
factory.GetType(),
controllerName));
}
return (ControllerBase)controller;
}
}
Html Helpers
public static class SecurityTrimmedLink
{
public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName)
{
return htmlHelper.HasActionPermission(actionName, "")
? htmlHelper.ActionLink(linkName, actionName)
: MvcHtmlString.Create("");
}
public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, RouteValueDictionary routeValueDictionary )
{
return htmlHelper.HasActionPermission(actionName, "")
? htmlHelper.ActionLink(linkName, actionName, routeValueDictionary)
: MvcHtmlString.Create("");
}
public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, object routeValues, object htmlAttributes )
{
return htmlHelper.HasActionPermission(actionName, "")
? htmlHelper.ActionLink(linkName, actionName, routeValues, htmlAttributes)
: MvcHtmlString.Create("");
}
public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, string controllerName)
{
return htmlHelper.HasActionPermission(actionName, controllerName)
? htmlHelper.ActionLink(linkName, actionName, controllerName)
: MvcHtmlString.Create("");
}
public static MvcHtmlString SecurityTrimmedActionLink(this HtmlHelper htmlHelper, string linkName, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
return htmlHelper.HasActionPermission(actionName, controllerName)
? htmlHelper.ActionLink(linkName, actionName, controllerName, routeValues, htmlAttributes)
: MvcHtmlString.Create("");
}
}
UWAGA: To nie zadziała w MVC 5, ponieważ wywołanie FindAction() nigdy nie zwraca deskryptora akcji
Próbowałem znaleźć problem, ale nie mogłem i skończyło się na programowaniu pracy. :(
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-18 22:45:38
Kod Jfara działał dla mnie przez większość czasu, ale musiałem wprowadzić pewne modyfikacje dla MVC4. Jest to jedyna metoda, która musiała się zmienić:
private static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false; // action does not exist so say yes - should we authorise this?!
AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);
// run each auth filter until on fails
// performance could be improved by some caching
foreach (var filter in FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor))
{
var authFilter = filter.Instance as IAuthorizationFilter;
if (authFilter == null)
continue;
authFilter.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-26 20:14:14
Część obszaru jest nieco bardziej skomplikowana niż dodawanie pewnych przeciążeń. UseNamespaceFallback hack nie działa, ponieważ będzie instantiate the wrong, gdy masz identycznie nazwanych kontrolerów w różnych obszarach.
Musisz mieć sposób, aby uzyskać poprawną przestrzeń nazw dla obszaru
Inaczej to
IController controller = factory.CreateController(helper.ViewContext.RequestContext, controllerName);
Pójdzie źle.
Obecnie mam takie linki w widoku
@Html.SecurityTrimmedActionLink("this link", "Index", "Home",new {Area=string.Empty});
@Html.SecurityTrimmedActionLink("this link", "Index", "FunctionAdministration", new {Area="Administration" }, null);
Wewnątrz
public static bool HasActionPermission(this HtmlHelper htmlHelper, string actionName, string controllerName, object area)
Dostanę przestrzeń nazw dla obszar lub domyślna przestrzeń nazw, gdy obszar jest pusty.
private static string GetNamespaceForArea(string area, RouteCollection routeColl)
{
string ns = string.Empty;
foreach (RouteBase routeBase in routeColl)
{
if (routeBase.GetType() == (typeof (Route)))
{
Route route = (Route) routeBase;
RouteValueDictionary dataTokens = route.DataTokens;
;
if (area != null)
{
if (dataTokens.ContainsKey("area"))
{
if (area.Equals(dataTokens["area"]))
{
ns = (string) ((string[]) dataTokens["Namespaces"])[0];
break;
}
}
else
{
if (area.Equals(string.Empty))
{
ns = (string) ((string[]) dataTokens["Namespaces"])[0];
break;
}
}
}
}
}
return ns;
}
Musisz skonfigurować domyślną przestrzeń nazw w swojej trasie w globalasax na przykład w ten sposób (domyślnie NS " ActionLinkTest.Kontrolery"):
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
, new[] { "ActionLinkTest.Controllers" }
);
Użyj go do utworzenia kontrolera na podstawie nazwy typu:
ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName) ? htmlHelper.ViewContext.Controller :(ControllerBase) Activator.CreateInstance(Type.GetType(type));
In global.asax Definiuj obszary
areaRegistration.Add("Administration","Areas.Administration");
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-03-22 10:08:09