Ok, I want to add some security to my site via the ActionLink method. If the user has enough rights to access the action/controller then the ActionLink should render the link. If not, It should return an empty string. Now, the ActionLink is a static method and that makes it all more difficult. Is there any way to achieve what im trying to do?
How to override the ActionLink behavior
Asked Answered
new AuthorizeActionLink
extension method. Overload as needed.
public static MvcHtmlString AuthorizeActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, object routeValues, object htmlAttributes)
{
if (HasActionPermission(helper, actionName, controllerName))
return helper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
return MvcHtmlString.Empty;
}
public static MvcHtmlString AuthorizeActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, RouteValueDictionary routeValues, IDictionary<string, object> htmlAttributes)
{
if (HasActionPermission(helper, actionName, controllerName))
return helper.ActionLink(linkText, actionName, controllerName, routeValues, htmlAttributes);
return MvcHtmlString.Empty;
}
methods that do the dirty work in figuring out if the user is Authorized
static bool HasActionPermission(this HtmlHelper htmlHelper, string actionName, string controllerName)
{
ControllerBase controllerToLinkTo = string.IsNullOrEmpty(controllerName)
? htmlHelper.ViewContext.Controller
: GetControllerByName(htmlHelper, controllerName);
ControllerContext controllerContext = new ControllerContext(htmlHelper.ViewContext.RequestContext, controllerToLinkTo);
ReflectedControllerDescriptor controllerDescriptor = new ReflectedControllerDescriptor(controllerToLinkTo.GetType());
ActionDescriptor actionDescriptor = controllerDescriptor.FindAction(controllerContext, actionName);
return ActionIsAuthorized(controllerContext, actionDescriptor);
}
static bool ActionIsAuthorized(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (actionDescriptor == null)
return false;
AuthorizationContext authContext = new AuthorizationContext(controllerContext, actionDescriptor);
foreach (IAuthorizationFilter authFilter in actionDescriptor.GetFilters().AuthorizationFilters)
{
authFilter.OnAuthorization(authContext);
if (authContext.Result != null)
return false;
}
return true;
}
static ControllerBase GetControllerByName(HtmlHelper helper, string controllerName)
{
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;
}
so there is no way to do it with the same Html.ActionLink() method? –
Tough
no, but I think this is a great reusable way to do this type of conditional link display across your whole application –
Hannan
@Hannan if we have edit link on users with 15 users listed on the page this whole code would execute 15 times. isn't it better to just calulate haspermission value in controller and push it on the view through some viewmodel –
Richmound
@Muhammad There's no reason this couldn't be extended by adding the result of
HasActionPermission
in HttpContext.Items
for any given action/controller combination. That's actually a good idea. –
Hannan @Hannan can you give me a starting point, i mean how to approach it –
Richmound
in the
HasActionPermission
do a check against the HttpContext.Items
colletion to see if the action
and controller
exist. If so, return that, if not, continue and check, then save to the context. –
Hannan © 2022 - 2024 — McMap. All rights reserved.
Authorize
attribute from the class/ method before rendering the link at all. So you can control the visibility of the link and access to the method with nothing more than the Authorize attribute. – Hannan