How to check current user's role in ASP.NET MVC 5 - c#

currently dealing with an interesting problem. I am building a website with three different user roles. When logged in, the MVC partial view shows navigation options for the users. I want to show different options depending on the user's role. In previous websites, I have used the following code to determine a role:
#if (Roles.IsUserInRole("intern"))
{
<li>#Html.ActionLink("Log Time", "Index", "Time")</li>
}
Unfortunately, when I attempted this in my current code, I got the message:
The Role Manager feature has not been enabled.
So apparently in the new MVC they disable the role manager by default and have a new way of doing it. No biggie. Searching the issue suggested that I enable the feature in web.config. I followed several instructions on how to do that (I promise I can google search) but it seems to mess with my SQL Server connection string, giving me errors that indicate it's trying to log in to a local db that doesn't exist rather than my Azure SQL Server. I've played around for a while and I don't know why this is the case.
Anyway, long story short, rather than work around and re-enable a vestigial Identity feature, how are you supposed to accomplish this in the new MVC? I can get the roles fine controller side with user manager, but I can't use that in a view. Similarly a Viewbag full of roles can't work because this is navigation on every page.
I appreciate all the help in advance, thanks everyone!

Just found the answer, I'll leave this up for other people dealing with this. The correct way to do this is:
#if (User.IsInRole("intern"))
This makes sense since MVC is moving away from Role based objects and towards User based objects.

I think it's not really a good idea to ask for the user's role all the time, too many requests are made.
It would be better to ask once and save it on a variable in Razor. Then just check that variable whenever you need it.
By the way, if the roles are different, you don't even want to ask if the user is in that role rather than another one. Rather get the list of roles in a list and check if the role indicated is in the list.
Example (I'm not sure it will compile, look a the idea):
#using Microsoft.AspNetCore.Identity
#using Microsoft.AspNetCore.Mvc.Localization
#inject UserManager<ApplicationUser> UserManager
#{
ApplicationUser currentUser = await UserManager.GetUserAsync(User);
var roles = await UserManager.GetRolesAsync(currentUser);
bool isIntern = roles.Contains("intern");
bool isExtern = roles.Contains("extern");
bool isFoo = roles.Contains("foo");
...
}
then, further on
#if (isIntern)
{
<li>#Html.ActionLink("Log Time", "Index", "Time")</li>
}
else if (isExtern)
{
...
}
You can control the role of the user as many times as you want without having to make other requests and it's all much more readable.

Related

Asp.net core 5, using Identity, how do I change the default redirect of the [Authorize] Attribute?

The [Authorize] is wonderful for locking pages down but I am building a new product with few users and it makes no sense that it directs people to Login, because there is no one to login yet. It should direct them to Register instead.
But I am struggling to find an easy way to do that without a ton of middleware.
You can change the LoginPath on start up, but I suspect this does not answer your question because when enough users exists then what happens?
To change the login path you can add:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(cookieOptions =>
{
cookieOptions.LoginPath = "/register";
cookieOptions.AccessDeniedPath = "/account/denied";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(120);
});
However if you are wanting a switch when you have reached a critical mass then this will not suffice.
EDIT one approach could be something like:
Create a loginOrRegister page. Then on this page hit the database (or whatever you use to see whether you have hit critical mass or its a known user based on a cookie) and then either
Redirect to Login
OR
Rediect to register

Multi-site, single code-base user registration using ASPNet.Identity

So I have multiple websites running off one code base (asp.net standard MVC). I'm using the built in ASPNet.Identity methods for users to register/log in (using ApplicationSignInManager, ApplicationUserManager).
Currently, all websites are using a single database to store user information. This is causing a couple of issues:
When user A registers on website A, they are now able to log into website B with the same details. As far as they are aware, they did not register on website B. Not good!
If I constrain user A to only access website A, if that user then tried to register on website B, they get the 'email address already in use' error. Also not good!
I've tried separating the databases, one per site, to get around this issue but I don't know how to dynamically change the DBContext assigned to the ApplicationSignInManager and ApplicationUserManager in my controller.
For example, when a user comes to website A, I grab the connection string for website A and perform login/register actions. I need the domain name to work out which connection string to load, which I can't access until after startup.cs code has run, configuring my manager instances.
I figure other people must have done this. Ideally I need to dynamically change the DBContext AFTER Startup.cs has run. Failing that, I need a nice approach to storing multiple identical email addresses in the same DB
Thanks
If you have a flag somewhere which website the current context is for, i'd say easiest to achieve that is to do two things:
extend the IdentityUser with a Website property, something simple either just an int WebsiteId or a String.
extend the AccountController to use that property wherever needed, I think you'd need to modify "Register" and all "Login" functions to verify the website the account is for.
I managed to find a solution. It's not the most elegant but it solves the issue until I can figure out how to dynamically change the DB Context used during login/register
In IdentityConfig.cs I switched 'RequireUniqueEmail' to false:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
Then when a user registers, I take the email address, make it unique to the website they registered on, and store it in the UserName field (I don't use the UserName for anything). When they log in, I make the same alteration to the entered email address before attempting login.
If this user registers on a different website, the username will be different even though the email is identical. Not perfect, but it works.
E.g.
string uniqueCode = "Website_Specific_String";
var user = new ApplicationUser { uniqueCode + model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
and the login
var user = await UserManager.FindByNameAsync(uniqueCode + model.Email);
Still open to better ideas. Thanks

ASP.NET MVC4 Authentication success but Roles are missing after project build

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()
Log off
}
</text>
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.

Altering database based on user id

I really looked, googled, this site, for a few days now, tried a bunch of different things, and I can't find an answer.
so, I'm trying to create a web application that will display client information after purchase. I'm using VS2012 express and C#, and I decided to use MVC4, mostly because there was a tutorial on ASP.NET that came pretty close to what I was looking to do. Any comments on my choices is not requested but also not unwelcome.
So the admin will enter all sales information at the end of each day. We record client phone numbers as account numbers in our sales protocol, so my thought was, to keep it simple, to just use the clients phone number as a login to the web application as well. Also, that way, when a client logs into the site to view the database, the database would filter automatically so that the particular client could only see their transactions.
The tutorial I followed is here.
I figured out that this is the point where the filter needs to be applied, but i'm having a lot of trouble doing so.
The controller is named "MainController"
The database is named "Main", table is "Mains"
"AccountNumber" is the field in the db that should match the Current User Id
public ActionResult Index()
{
return View(db.Mains.ToList());
}
As I understand it, I have to place [InitializeSimpleMembership] above, then grab the UserId, then define it as the filter.
First of all, you should decide one of these way:
1) keeping login and user info in a separate table and let the SimpleMembership(SM) does its default jobs.
2) Using an existing table so store users info and tell the SM which table is for.
Approach One:
To handle this approach, all you need is that you create users manually(as you do, I think) and add an extra line to the action method which is responsible of creating customers:
[HttpPost]
public ActionResult Create(Customer model)
{
if (ModelState.IsValid)
{
db.Customers.Add(model);
try
{
db.SaveChanges();
// here it is ...
WebSecurity.CreateUserAndAccount(model.Phone, model.Password);
}
catch
{
insertError = true;
}
// .. Other codes ...
}
Now, your customers can simply login to the site with their phone no. as username and that password.
And to retrieve items related to a specific user - which is currently logged into site - simply use the following query:
public ActionResult Index()
{
return View(db.Mains.Where(m => m.AccountNumber == User.Identity.Name)
.ToList());
}
If you also need approach two, tell me to update my answer and put it here

How to get a role using asp.net 4.0

I am new in ASP.NET 4.0 and C#..If I want hide/show menu item based on user logged in using web.sitemap, I must use a role and set it in web.config..I want to ask, where I can get that role?
if (User.IsInRole("rolename")) {
// what you wan't to do.
}
If you are really new to ASP.NET, you need to learn about Users and Roles. Try to use Membership API with standard elements like "Login". After that you have to write your own Users and Roles Provider with custom data structure. Then, use #Randolf R-F statement.
You need to use IPrincipal to store roles.
GenericIdentity userIdentity = new GenericIdentity((FormsIdentity)HttpContext.Current.User.Identity.Name);
string[] roles = { "rolename1", "rolename2", "rolename3" };
GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, roles);
Context.User = userPrincipal;
then you can check for user roles
if (User.IsInRole("rolename1")) {
// what you wan't to do.
}
If I understand correctly, you are saying that you have an asp.net web application project open and you want to know how to create a user and assign a role to them.
If you currently have web.config configured to use the default provider for rolemanager and membership provider then asp.net will take care of all the tricky stuff. The only things you have to do is go to your menu bar and select "Project -> ASP.NET Configuration". This will bring up a GUI for creating users and roles then assigning them. It should be a pretty self explanatory tool. That should meet your web.config use requirement too.
As suggested above from here if you have the user logged in you can do things like:
if (User.IsInRole("rolename"))
{ // what you wan't to do. }
Alternatively (and best for you atm) you should try using the logInView control in your toolbox.(I think that's what it's called) Do some googling on using these controls and it'll get you across the line.

Categories

Resources