I am working with a permission based authorization system for my app in ASP.NET MVC. For this I have created a custom authorization attribute
public class M
While I agree that generating urls based on permissions is probably not best practices, if you want to continue anyway you can find the actions and their attributes using these:
Retrieve 'Action' methods: This retrieves a collection of method infos because it is possible to have multiple Controller classes with the same name and multiple methods of the same name in particular with the use of areas. If you have to worry about this I'll leave the disambiguation up to you.
public static IEnumerable GetActions(string controller, string action)
{
return Assembly.GetExecutingAssembly().GetTypes()
.Where(t =>(t.Name == controller && typeof(Controller).IsAssignableFrom(t)))
.SelectMany(
type =>
type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(a => a.Name == action && a.ReturnType == typeof(ActionResult))
);
}
Retrieve Permissions from MyAuthorizationAttributes:
public static MyAuthorizations GetMyAuthorizations(IEnumerable actions)
{
var myAuthorization = new MyAuthorizations();
foreach (var methodInfo in actions)
{
var authorizationAttributes = methodInfo
.GetCustomAttributes(typeof (MyAuthorizationAttribute), false)
.Cast();
foreach (var myAuthorizationAttribute in authorizationAttributes)
{
myAuthorization.Roles.Add(MyAuthorizationAttribute.Role);
myAuthorization.Permissions.Add(MyAuthorizationAttribute.Permission);
}
}
return myAuthorization;
}
public class MyAuthorizations
{
public MyAuthorizations()
{
Roles = new List();
Permissions = new List();
}
public List Roles { get; set; }
public List Permissions { get; set; }
}
Finally the AuthorizedAction extension: warning:If you do have more than one match for a given controller/action pair this will give the 'authorized' url if the user is authorized for any of them...
public static string AuthorizedAction(this UrlHelper url, string controller, string action)
{
var actions = GetActions(controller, action);
var authorized = GetMyAuthorizations(actions);
if(user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission)))
{
return url.Action(controller,action)
}
return string.empty;
}
A Note on Generating Urls Based on Permissions:
I state this is probably not best practice because of many little things. Each may have their own level of relevance depending on your situation.
Controlling Page Rendering Via Action Authorization attributes:
Modify the AuthorizedAction method to be a boolean, then use the result of that to control page rendering.
public static bool AuthorizedAction(this HtmlHelper helper, string controller, string action)
{
var actions = GetActions(controller, action);
var authorized = GetMyAuthorizations(actions);
return user.Roles.Any(userrole => authorized.Roles.Any(role => role == userrole)) ||
user.Permissions.Any(userPermission => authorized.Permissions.Any(permission => permission == userPermission))
}
Then use this in your razor pages.
@if(Html.AuthorizedAction("User","Add")){
If you see this, you have permission to add a user.
}
else {
}