How to login with 3 parameters in asp.net mvc - asp.net-mvc-3

I have an application where one username can belong to many companies. Thus to distinguish them, i need to use the both username and password as unique pair to login.
I'm using ASP.NET MVC and i struggle to understand where the Login occurs.
Actually i can see where it validates the user but i don't find where it retrieves the user.
So where the
Select user where username=xx and password=xx occurs ?
Asked differently : i did not find wher User is set ? I see User.Identity.Name it in the code, but i don't see :
User=Select....
Thanks
John

John, as you are using MVC. You wont be seeing any queries in the code aside from the LINQ syntax. Im guessing what you are trying to do is a many to many relationship between the User table and the Company table. (one user has multiple companys and 1 company has multiple users)
Pretty much database wise this would mean you need an extra table with both primairy keys of Company and Users.
To get back to your question. ASP.net MVC has its own membership provider. You can choose to either use the default one with its own tables or overwrite it and create ur own custom membership provider (with the ability to use ur own user table)
The default one pretty much should contain most of the basic attributes. (password reset, password salt, email,..)
http://www.asp.net/web-forms/tutorials/security/membership/creating-the-membership-schema-in-sql-server-cs
skip to the step: Installing the Application Services to generate the tables
However guessing you already have a database with your very own user table. you should overwrite the custom membership class.
Simply this would mean you make a new class that inherits from the abstract class "MembershipProvider"
public class MyMembershipProvider : MembershipProvider
{
}
After that you have to let asp know that you will be overwriting the default membershipprovider with yours in web.config:
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider"
applicationName="MyApp"
Description="My Membership Provider"
passwordFormat="Clear"
connectionStringName="MyMembershipConnection"
type="MyApp.MyMembershipProvider" />
</providers>
</membership>
Some methods in the membership provider requires you to return or use an object of MembershipUser. Everything of how to implement this is right here:
http://msdn.microsoft.com/en-us/library/system.web.security.membershipuser.aspx
This is not a necessary step but its recommended.
Good luck john :)
If theres any confusion in the explanation, dont hesitate to ask

Short Answer:
The code you're describing happens behind the scenes in the LogOn action of the Account Controller:
MembershipService.ValidateUser(model.UserName, model.Password)
Which returns true for a valid user. The user is then "signed in" with the next line in the code:
FormsService.SignIn(model.UserName, model.RememberMe)
(You can see both of those functions defined in the AccountModels file under the Models folder)
If you want to also check company id while authenticating the user then you'll need to write your own auth method to replace ValidateUser. Ths will depend on what you're using for your store (SQL?)
But, as a broader point, best practices you should not allow the same user name for different users. It's just a bad idea and will lead to trouble.
UPDATE:
If I were recommending how to do this, I would suggest you user the UserProfile aspect of ASP.NET Membership. It is designed to capture and store additional user variables (such as company) while still using the nicely built and secure Membership that they've written for you. Read up on it, but below is my CreateUser function in the app I'm currently working on. Note how I use the Profile to store first and last name as well as a flag that the user needs their password reset.
Again, this would preempt the ability to have multiple users with the same username, but I really think you ought to avoid that.
[HttpPost]
public ActionResult CreateUser(string username, string email, string first, string last, string role)
{
string password;
MembershipUser user;
//Generate a random password
password = Auth.CreateRandomPassword(6);
try
{
//Create the user
user = Membership.CreateUser(username, password, email);
//Add the user to the chosen role
Roles.AddUserToRole(username, role);
//Create the user profile
UserProfile profile = UserProfile.GetUserProfile(username);
profile.FirstName = first;
profile.LastName = last;
profile.ForcePasswordReset = true;
profile.Save();
EmailNewUser(username, email, password);
}
catch (Exception ex)
{
HttpContext.Response.StatusCode = 500;
HttpContext.Response.StatusDescription = ex.Message;
HttpContext.Response.Clear();
}
return PartialView("UserTable", Auth.Users());
}

Related

Transparently customizing logic based on the user role

I have a set of RESTful services that mostly return data. The trick is that I want to return all data (e.g. via findAll()) for an admin user, and only the entities that belong to the current user for non-admin (e.g. findAllByUser(currentUser)). I feel like there might be a scalability problem doing it this way:
#GetMapping("/statuses")
public ResponseEntity<Page<Status>> getAllStatuses(Pageable pageable) {
Page<Status> page;
if (SecurityUtils.isAdmin()) {
page = statusRepo.findAll(pageable);
} else {
page = statusRepo.findAllByUser(getCurrentUser(), pageable);
}
return ResponseEntity.ok().body(page);
}
What would be a good generic solution / design pattern to split it up? Access-control-lists seem pretty complicated. Building separate resources for admin / non-admin still duplicates the REST endpoints. May be build some kind of interceptor that pick the right DAO call based on a role would do?
Any help is greatly appreciated.

How can I make the [Authorize] attribute more flexible?

I have an MVC 5 application that I lock down by only allowing certain authenticated users to have access to specific actions within my controller. I utilize the authorize attribute at the top of the class allowing only the user(s) I want to gain access after login. I do this with the following attribute placed at top of my class...
[Authorize(Users="user1,user2")]
This works great! However, what if I don't want to recompile and deploy the application everytime I want to add a new user to this specific controller?
I thought I might add this in my web.config file under as a key like so...
<appSettings>
<add users="user1,user2"/>
</appSettings>
But when I try to access this key in my controller like so: [Authorize(Users=ConfigurationManager.AppSettings["users"])] I am getting an error: Cannot resolve symbol 'AppSettings'.
Is there a way to do this?
I'm not sure why an answer that didn't answer the question was accepted. Regardless, I thought it might be worth adding an answer for any future travelers.
While this functionality isn't provided out of the box, it's certainly possible by writing your own authorize attribute.
public class ConfigAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var allowedUsers = ConfigurationManager.AppSettings["CoolActionAllowedUsers"];
var allowedUsersArray = allowedUsers.Split(',');
if (httpContext.User.Identity != null && allowedUsersArray.Contains(httpContext.User.Identity.Name))
{
return true;
}
return false;
}
}
And to use the attribute:
[ConfigAuthorize]
public ActionResult CoolAction() {
//...
}
In the code above when your authorization is performed in AuthorizeCore, the configuration value from CoolActionAllowedUsers will be pulled into memory and the currently authenticated user will be verified if they are in the list of allowed users. If you make a change to your config file it won't be a problem; the application pool will automatically restart and the next time the code runs to read the config file your new value will be read.
I completely agree with #Shoe that roles should be used. Managing a list of users in your code is just a pain in the arse. In fact, at work, anytime I get a request for just one random user to have access to a page I always require a group to be setup. However the code above could apply to a list of roles as well.
Instead of using the Users parameter use the Roles parameter.
[Authorize(Roles="CanExecuteActions")]
Now you can manage what users have access to your controller by giving them this role. Any user without the role can't execute any actions of the controller.

Manage multiple calls of Membership.GetUser().ProviderUserKey;

I was thinking yesterday how to solve this issue, because everything what i give or check about user is depended of his ProviderUserKey (ID).
So i made one static function like
public static Guid GetUserID()
{
string UserID = string.Empty;
if(HttpContext.Current.Session["UserID"] != null)
{
UserID = HttpContext.Current.Session["UserID"].ToString();
}
if(!string.IsNullOrEmpty(UserID))
{
return new Guid(UserID);
}
UserID = Membership.GetUser().ProviderUserKey.ToString();
HttpContext.Current.Session["UserID"] = UserID;
return new Guid(UserID);
}
Main point of this class is to reduce database connections to check/get user ID.
My problem with this function is not that this is not working, my problem is what if logged user log out and log with another account?
Or Is it better to add session value on log in and clear session value on log out?
Where you can see any other problem with this kind of "Get User ID"?
If you log the user out then you should also be killing the session.
When you login as another user you would also have the session reinitialized.
Note you'll want to keep the session and forms auth timeouts (assuming you are using forms auth) in sync with each other:
How can I handle forms authentication timeout exceptions in ASP.NET?
This should help keep the session in line with the forms auth token. You'll in turn need to kill the session on logout and intialize it upon login.
Another alternative is to implememt your own membership provider that caches this key to prevent constant db hits.
have you tried using the ProfileProvider?
you can use and customize with special properties and that's is managed by session each user.
example to get values:
HttpContext.Profile.GetPropertyValue["CustomProperty"]
In this video you can lean to implement it, create, configure and use...
http://www.asp.net/web-forms/videos/how-do-i/how-do-i-create-a-custom-profile-provider

Creating a unique ID in a form

I have a form that I have users fill out and then it gets e-mailed to me.
I am trying to get an example of how I would create an ID (based on my own conventions) that I can use to keep track of responses (and send back to the user so they can reference it later).
This is the convention I am striving for:
[YEAR]-[SERVICE CODE]-[DATE(MMDD)]-[TIME]
For example: "2012-ABC-0204-1344". I figured to add the TIME convention in the instance that two different users pick the same service on the same date rather than try to figure out how to only apply it IF two users picked the same service on the same date.
So, the scenario is that after the user goes through my wizards inputting their information and then click "Submit" that this unique ID would be created and attached to the model. Maybe something like #Model.UniqueID so that in an e-mail response I send to the user it shows up and says "Reference this ID for any future communication".
Thanks for any advice/help/examples.
In your post action
[HttpPost]
public ActionResult Create(YourModel model)
{
model.UniqueId = GenerateUniqueId(serviceCode);
}
public string GenerateUniqueId(string serviceCode)
{
return string.Format("{0}-{1}-{2}", DateTime.Now.Year, serviceCode, Guid.NewGuid().ToString().Replace("-",""); //remove dashes so its fits into your convention
}
but this seems as I'm missing part of your question. If you really want unique, use a Guid. This is what we've used in the past to give to customers - a guid or a portion of one. IF you use a portion of one ensure you have logic to handle a duplicate key. You don't need to worry about this though if using a full guid. If the idea is just to give to a customer then ignore the rest of the data and just use a guid, since it can easily be looked up in the database.

Why doesn't ValidateUser return more?

I'm using the standard .NET membership provider, and thought I'd see if someone could shine a light on it.
Calling ValidateUser return either true or false. Now since the method accepts a username and password, one would reason that the return result would reflect an invalid username or password. Yet if we delve into it further, we find it is also checking IsLockedOut and IsApproved.
public override bool ValidateUser (string username, string password)
{
MembershipUser user = GetUser (username, false);
/* if the user is locked out, return false immediately */
if (user.IsLockedOut)
return false;
/* if the user is not yet approved, return false */
if (!user.IsApproved)
return false;
......
In my application, I would like to make use of IsApproved for my own means. Simply rolling my own provider won't work because I'm still constrained to a bool result. Creating a user gives us all the information we need, so why not ValidateUser? Am I missing something?
I expect that what you're seeing is a security decision - by restricting the information returned, they're not providing information to malicious parties.
Imagine that you're Cyril Cracker, trying to break into a website.
Scenario #1:
You try entering username "Admin" with password "Password" and the system tells you no-go. The only information you have is that Admin/Password isn't a valid combination.
Scenario #2:
You try entering username "Admin" with password "Password" and the system tells you that no user by that name is known. You can keep trying different usernames until you find one that is known.
Scenario #3: You try entering username "Admin" with password "Password" and the system tells you that no the password is invalid. Suddenly, you know that "Admin" is a valid user. You've learnt something useful, and all you need to keep guessing is the password.
Scenario #4: You try entering username "Admin" with password "Password" and the system tells you that the account is blocked. Now, you know a valid username and password, and that the account is blocked. You can come back later and try again.
Systems that spill the beans on what's valid, and what's not, are known as chatty systems, and they're considered insecure for good reason, as they're easier to crack.
Hope this is helpful.

Resources