Reputation: 152
I have searched through many SO and other pages but still couldn't find a similar case.
My application is using SimpleMembership. It has role-based user menu and most actions are with [Authorize(Roles = "aaa,bbb")] attribute.
After the application build or leaving the browser idle over some time, user will be redirected to login page from whatever link is clicked. Strange thing is that the login name is still valid to be shown but its without the role names it attached.
Users are able to access actions with [Authorize] attribute in the above situation. I am wondering if there is anything I am missing, or I can do to keep the role information alive as long as the authentication is valid?
Here is code from the controller action to render the menu:
public ActionResult Menu() { if (Roles.IsUserInRole("Admin")) { return View("MenuAdmin"); } if (Roles.IsUserInRole("Advising")) { return View("MenuAdvising"); } if (Roles.IsUserInRole("StudentDevelopment")) { return View("MenuStudentDevelopment"); }...
Customized _logonPartial to display the login name and roles
> @if (Request.IsAuthenticated) {
<text>
Logged in: @Html.ActionLink(User.Identity.Name, "Manage", "Account", routeValues: null, htmlAttributes: new { @class = "username", title = "Manage" }) as
@String.Join(",", Roles.GetRolesForUser(User.Identity.Name))
@using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" })) {
@Html.AntiForgeryToken()
<a href="javascript:document.getElementById('logoutForm').submit()">Log off</a>
}
</text>
Upvotes: 0
Views: 1004
Reputation: 17540
The role information is available as long as the user is logged in. In your case you are calling:
Roles.GetRolesForUser(User.Identity.Name)
to get the list of roles. This actually hits the database to get the roles, so as long us the user name is available in Identity it will return roles, assuming that the user has roles assigned to them. And the user name should be available if the user id authenticated. You are checking Request.IsAuthenticated. It is probably better to check User.Identity.IsAuthenticated.
There is a way to get the roles without querying the database using the claims. And in general I would not put such logic and access to system variables in your Views. I would use this approach in my controller action to get the roles.
var prinicpal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var roles = prinicpal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value);
ViewBag.Roles = roles;
Either put the information you need in your Views in the the ViewBag or create a model that is passed to the View. This approach for getting roles does not require a round trip to the database.
Update to Address Comments
The standard AuthorizeAttribute for MVC 4 uses the configured membership and role provider (in your case SimpleMembership) to get the roles. It does not assume claims where used so it goes to the database every time. In fact it has to go the database twice, once to get the user ID for the logged in user and then to get the roles based on that user ID. Not very efficient. Surprisingly it does this even if you do not pass in any roles as parameters, where it does not need to authorize the user against roles and should just authenticate. I verified this with a profiler.
To make it more efficient and not require going to the database you could write a custom AuthorizeAttribute to use claims instead. Here is an article that describes how to do this, with links to example source code.
I cannot explain the scenario you are describing, which occurs right after a build. But if you create a custom attribute and use it instead you will be able to debug and see what exactly is going on. Plus I would not worry too much about it as it should not be a typical scenario you would see in production.
Upvotes: 1