Add User Roles on Registration (Forms Authentication) MVC3 - asp.net-mvc-3

I am developing an MVC 3 project and want to add a user to a role when they are registered, using Forms Authentication. So I'd like to create some check boxes, or a drop down list showing the roles, which are selected and the user is assigned to the role as they are registered.
I have this code so far, which works:
public ActionResult Register()
{
ViewData["roleName"] = new SelectList(Roles.GetAllRoles(), "roleName");
return View();
}
And in the view I have:
<label for="roleName">Select Role:</label>
#Html.DropDownList("roleName")
#Html.ValidationMessage("roleName")
This is HttpPost section of the controller, and this is the part that I don't know what to code:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, null, null, true, null, out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
// If we got this far, something failed, redisplay form
return View(model);
So, all the roles do show up in the view. All I need to know is what to add to the HttpPost section to get this working.
Thanks a lot, Amy

if (createStatus == MembershipCreateStatus.Success)
{
Roles.AddUserToRole(model.UserName, "RoleName");
FormsAuthentication.SetAuthCookie(model.UserName, false /* createPersistentCookie */);
return RedirectToAction("Index", "Home");
}
Try this. It's should work

Related

redirecting authorized users to certain pages

I used the identity to assign roles to users.now I want to redirect users according to their role to certain pages.for example user with role"user" after login redirects to a page says" hello user".different from that of admin and so on.I have created the pages and validate authorization each .but where should I redirect after login?
You can simply change the login post action to check the user and redirect to the correct view.
Create separate view for normal users.
NormalUser.cshtml
#{
ViewBag.Title = "Normal User";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Hello User!</h2>
Inside the controller
[Authorize(Roles = "user")]
public ActionResult NormalUser()
{
ViewBag.Message = "Hello normal user.";
return View();
}
Then Login post action
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
var user = await UserManager.FindByEmailAsync(model.Email);
var userRole = await UserManager.GetRolesAsync(user.Id);
if (userRole.Any(role => role == "user"))
{
RedirectToAction("NormalUser", "Home");
}
else
{
RedirectToAction("Index", "Home");
}
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Check this github Project to get the code.

Request.IsAuthenticated Return False all the time

I am having an issue with my Request.IsAuthenticated always return false. I am setting the AuthCookie
CurrentRequest currentRequest = null;
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
} else if (login.ValidateUser(acct.UserName, acct.Password))
{
FormsAuthentication.SetAuthCookie(acct.UserName, true); //Edit on 11/12 #11:08
currentRequest = new CurrentRequest();
SessionWrapper.currentRequest = currentRequest;
return RedirectToAction("About", "Home");
}
//This is a partial login page that is supposed to display login or Logoff.
#using KMSS.Helper;
// this is always false
#if (Request.IsAuthenticated) //Same issue with User.Identity.IsAuthenticated
{
if (SessionWrapper.currentRequest != null)
{
<text> Welcome <strong> #SessionWrapper.currentRequest.Username </strong>
[#Html.ActionLink("Sign Off", "Logoff", "Account")]
</text>
} else {
#: [ #Html.ActionLink("Sign In", "Login", "Account") ]
}
} else
{
#:[ #Html.ActionLink("Sign In", "Login", "Account") ]
}
After reading online, I created a class with a bool value and tries to use that class instead. However, I am getting the object is not set to instance of a new variable exception.
Here is how I had it set up:
//Partial Login page
#model KMSS.Helper.ViewModelAuthenticate;
// this is always false
#if (Model.IsAuthenticated)
//The model is null even though I create create a reference in the Login Method i.e.
(ViewModelAuthenticate auth = new ViewModelAuthenticate();
{
if (SessionWrapper.currentRequest != null)
{
<text> Welcome <strong> #SessionWrapper.currentRequest.Username </strong>
[#Html.ActionLink("Sign Off", "Logoff", "Account")]
</text>
} else {
#: [ #Html.ActionLink("Sign In", "Login", "Account") ]
}
} else
{
#:[ #Html.ActionLink("Sign In", "Login", "Account") ]
}
//Here is the class
public class ViewModelAuthenticate
{
public bool IsAuthenticate { get; set; }
}
//Here is where I am initializing the class in the controller
public ActionResult Login()
{
ViewModelAuthenticate auth = new ViewModelAuthenticate();
auth.IsAuthenticate = false;
return View();
}
//I tried this inside and outside of Login, and it is called before the partial login view. However, I am still getting the object is not set to instance of a new variable exception.
What am I doing wrong here? Your help will be appreciated.
//Showing the authentication section of the config file.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" slidingExpiration="true" />
</authentication>
I replaced my authentication section with this sample that a sample that I found here. It is working now.
Looking at your code, I feel that there is more going on here than you are showing us. Specifically, the variables CurrentRequest and SessionWrapper, setting them to null on the beginning of the Action method call, etc. I would suggest trying a basic, bare bones example in your project and then begin to add items back in as needed. No AJAX, only full page post back to the server from your login form. Such an example would look like:
Login View Model
public class LoginViewModel{
[Required]
public string UserName {get;set;}
[Required]
public string Password {get;set;}
}
Login POST Action method
[HttpPost]
public ActionResult Login(LoginViewModel model, string returnUrl){
if(!ModelState.IsValid){
return View();
}
if(!provider.ValidateUser(model.UserName, model.Password){
ModelState.AddModelError("", "The username/password combination does not match");
return View();
}
FormAuthentication.SetAuthCookie(model.UserName, true);
if(!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl){
return Redirect(returnUrl);
}
return RedirectToAction("About", "Home");
}
About View
#if(Request.IsAuthenticated){
<b>It WORKS!!</b>
}else{
<b>Nope, still not working</b>
}
I was testing and I set my time back a few days. For someone reason it caused this issue after putting the date back it was fine. I assume windows forms had the old date (which was todays date) cached so I assume it was expired. Just a thought about the matter.

Background Registration MVC3

Working on an MVC4 Application where users first fill in a form as the starting point. With this form there is a field to provide user email and a unique 9 digit number and other details. What I want to achieve is after submitting this form I want the user to be silently registered using the unique 9 digit number as username, and an auto-generated password hashed and saved as password in the membership(extended simplemembership) database. An email with password will be sent to the user afterwards. I will be grateful for any hints or help in this regards.
After the form is filled, I make the following redirection
return RedirectToAction("AutoRegister", new RouteValueDictionary(new { controller = "Account", action = "AutoRegister", Uname= ViewBag.Uname, Uemail = ViewBag.Uemail }));
and I have the following code in Account Controller
[AllowAnonymous]
public ActionResult AutoRegister()
{
return View();
}
[AllowAnonymous]
[HttpPost]
public ActionResult AutoRegister(RegisterModel model,string Uname, string Uemail)
{
//if (ModelState.IsValid)
if(Uemail!=null && Uname!=null)
{
string Upass = Membership.GeneratePassword(12, 1);
model.Email = Uemail;
model.UserName = Uname;
model.Password = Upass;
// Attempt to register the user
MembershipCreateStatus createStatus;
Membership.CreateUser(model.UserName, model.Password, model.Email, passwordQuestion: null, passwordAnswer: null, isApproved: false, providerUserKey: null, status: out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
FormsAuthentication.SetAuthCookie(model.UserName, createPersistentCookie: false);
return RedirectToAction("Welcome", "OnlineApplication");
}
else
{
ModelState.AddModelError("", ErrorCodeToString(createStatus));
}
}
I have also set the RouteConfig to accept the url. When I fill the form it redirects allright to a page with the url parameters populated but nothing happens. The user is not created. As for emailing of password I can fix that with no problems.
Could anyone assist on how I can commit the new user details in database

ASP.net MVC 3 customizing Html.ValidationMessageFor based on model logic

I've been reading over several of the questions similar to this, dealing with customizing the #Html.ValidationMessageFor but none of them touched on what I'm looking to do.
The current form I'm working on is editing a user in a database. Within this form I need to check that the email being entered is not already used for another user. I have the logic, but what I don't have is the custom validation message to appear on the page if they use an already in-use email.
Controller code:
[HttpPost]
public ActionResult EditUser(int id, EditUserModel model)
{
if (ModelState.IsValid)
{
tbl_Users editedUser = tblUsers.EditUser(id, model, HttpContext.User.Identity.Name);
tblHSDA.EditHSDAS(id, editedUser, model.hsdas, HttpContext.User.Identity.Name);
return Redirect("~/UserManage/ListActiveUsers");
}
if (tblUsers.ValidateEmailInUse(model.Email))
{
// change validation message and return View(model);
}
tbl_Users tbl_users = db.tbl_Users.SingleOrDefault(item => item.User_id == id);
ViewBag.hsdas = tblHSDA.GetHSDANameAlpha();
ViewBag.Username = tbl_users.Username;
return View(model);
}
Is this something done at the Controller level?
as per your logic the email check part will never execute if the user fills in the form correctly and provides a duplicate email
what you can do is change the ActionResult like
[HttpPost]
public ActionResult EditUser(int id, EditUserModel model)
{
if (ModelState.IsValid)
{
if(!CheckEmail(model.Email)){
tbl_Users editedUser = tblUsers.EditUser(id, model, HttpContext.User.Identity.Name);
tblHSDA.EditHSDAS(id, editedUser, model.hsdas, HttpContext.User.Identity.Name);
return Redirect("~/UserManage/ListActiveUsers");
}else{
ModelState.AddModelError("Email","Email provided is already in use...")
}
}
tbl_Users tbl_users = db.tbl_Users.SingleOrDefault(item => item.User_id == id);
ViewBag.hsdas = tblHSDA.GetHSDANameAlpha();
ViewBag.Username = tbl_users.Username;
return View(model);
}
private bool CheckEmail(string email){
//email check logic
// return true or false
}
also have a look at http://msdn.microsoft.com/en-us/library/gg508808%28v=vs.98%29.aspx

MVC Role based routing clarification

Small Question at the end of a very long explanation ...
Assuming the Admin User belonging to Admin Role and the Regular User belonging to User Role attempt to access the Index page with the following route registered in Global.asax.
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new[] {"tst.Controllers"}
);
In the HomeController, the Index Action Method is decorated with the Authorize attribute.
[Authorize]
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
Forcing the anonymous user to logon.
If the Admin User logs in with his/her credentials, I would like to redirect him/her to the Index Action Method in the HomeController located in the Admin area.
If a Regular user logs in, I would like to redirect him/her to the Index Action Method in the HomeController located in the User area.
I have the following code in UserAreaRegistration.cs
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"User",
"Profile/{action}",
new { area = AreaName, Controller = "Home", action = "Index" },
new { RoleConstraint = new RoleConstraint()},
new[]{ "tst.Areas.User.Controllers"}
);
}
and the following code for AdminAreaRegistration.cs
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin",
"Profile/{action}",
new { area = AreaName, Controller = "Home", action = "Index" },
new { RoleConstraint = new RoleConstraint()},
new[]{ "tst.Areas.Admin.Controllers"}
);
}
Where the RoleConstraint is defined as follows
public class RoleConstraint: IRouteConstraint
{
public bool Match(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
RoleProvider rp = new tst.Providers.CustomRoleProvider();
string[] roles = rp.GetRolesForUser(httpContext.User.Identity.Name);
if (roles != null && roles.Length > 0)
{
string roleName = roles[0];
string areaName = route.Defaults["area"].ToString();
return areaName == roleName;
}
return false;
}
}
The stock standard LogOn Action Method in the AdminController in the main Controllers folder...
[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(returnUrl)
&& returnUrl.Length > 1
&& returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//")
&& !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
Question:
Am I right in thinking that, When the Admin/Regular User is validated he/she must be redirected in this line in the code snippet above
return RedirectToAction("Index", "Home");
to the appropriate Index Action Method(Read: Index Action Method in the appropriate Area).
If so, I would like to know how.
I am confused because a constant string "Profile" is involved and it is not the usual stuff involving an action method and a controller name. "Profile" is neither a controller nor an action method.
Inspired by this post
MVC role-based routing
Instead of
return RedirectToAction("Index", "Home");
in the LogOn Action Method, I replaced it with
return Redirect("/Profile");
It worked !!!
However, what I don't understand is, when I click Log Off, it renders the Index page in the main Views folder. So I have to click LogOff again to be taken back to the LogOn page.

Resources