How to use MembershipUser.IsApproved for account confirmation - asp.net-mvc

Here's what I want to achieve with ASP.Net MVC 3 :
Before a user can actively use my website, I want them to fill a form and give a few details. Unless they have done so, they get redirected to the form website.
I thought this would be feasible by registering the account with the IsApproved-flag set to false, but actually such users cannot login at all (Membership.ValidateUser(model.UserName, model.Password) always returns false).
Is there any mechanism in forms authentication that supports this scenario? Or will I have to track this in a custom database table?
Thanks in advance!
EDIT : Thanks to MystereMan (read his reply first) and a few other great posts here on SO, here's the ActionFilter (Attribute and its usage):
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class RedirectUserInRoleAttribute : ActionFilterAttribute
{
public string RoleName { get; set; }
public string TargetControllerName { get; set; }
public string TargetActionName { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
MembershipUser user = Membership.GetUser();
if (user != null)
{
string[] roles = Roles.GetRolesForUser(user.UserName);
if (roles.Any(x => x == RoleName))
{
RouteValueDictionary redirectTargetDictionary = new RouteValueDictionary();
redirectTargetDictionary.Add("action", TargetActionName);
redirectTargetDictionary.Add("controller", TargetControllerName);
filterContext.Result = new RedirectToRouteResult(redirectTargetDictionary);
}
}
}
}
[Authorize]
[RedirectUserInRole(RoleName = "Pending", TargetControllerName = "Home", TargetActionName = "Index")]
public ActionResult ChangePassword()
{
//...
}
[Authorize]
[RedirectUserInRole(RoleName = "Member", TargetControllerName = "Home", TargetActionName = "Index")]
public ActionResult Confirm()
{
// show confirm form here!
}

I think the easiest solution would be to create a "Pending" membership role. When you first create the account, assign them to the "Pending" role. Then, you can create a Action filter that will redirect all users in the Pending role to your form.
EDIT:
A few gotchas to watch out for:
Make sure you reassign them to a different role after they've completed the form
Make sure you don't redirect them on the form action itself, or it's post action (otherwise you will be in an infinite loop)
You may want to prevent users in other roles from navigating to the form.

You have to track that information by yourself. You can create an own table as you suggested and link it to the aspnet_users table or add a column to the aspnet_users table.

Related

MVC implement security to prevent accessing other users information

I am familiar with roles and authentication attributes in MVC, but as I am adding more and more information onto my database I think I ma going to run into a problem with primary keys being unencrypted or accessible.
I am using identity 2.1, so when a user is logged in I have access of their UserId and their CustomerID but my concern is that any user can go to /Customers/Delete/3 or any CustomerID and have access. Even if I created a GUID id or other encryption it could still be vulnerable to brute force attacks.
Is there a way in MVC to implement a check to only allow the current user to load pages that are related to them?
You can add extra field say "CreatedByUserId" to database table and when user access page check if CreatedByUserId matches with user id of logged in user or not.
You should be checking if the current logged in user has access to any of the information before you try and manipulate data. For example...
public async Task<HttpResponseMessage> DeleteCustomer(string customerId)
{
var appUser = await _authRepository.FindUser(User.Identity.GetUserName());
if(!_customerRepository.CanDeleteCustomer(appUser.Id, customerId){
return BadRequest();
}
// they have access so do what you need to do down here..
}
You can create a custom Authorize Attribute and a table in the database in which you store which user is allowed what Pages (Actions) or Controllers and then check that table while authorizing that whether the user is authorized for that Page/Controller. I have created an example for you in which I used Custom Authorize Attribute named MyAuthorizeAttribute and a database table named PageRoles.
Custom Authorize Attribute:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
readonly ApplicationDbContext _db = new ApplicationDbContext();
string _pageName;
public MyAuthorizeAttribute(string pageNameFromController)
{
_pageName = pageNameFromController;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var userId = httpContext.User.Identity.GetUserId();
var pageRoles = db.PageRoles.Where(m => m.UserId == userId);
foreach (var item in pageRoles)
{
if (item.PageName == _pageName && item.UserId == userId)
{
return base.AuthorizeCore(httpContext);
}
}
return false;
}
}
Model used:
public class PageRole
{
public int Id { get; set; }
public string UserId { get; set; }
public string PageName { get; set; }
public virtual ApplicationUser User { get; set; }
}
and then you will just have to use the attribute on your controllers just like you use Authorize attribute:
[MyAuthorize("Home")]
public class HomeController : Controller
{ }

How to handle role authorization for users having multiple roles in ASP.NET MVC?

I am trying to find the best way to structure my security roles in ASP.NET MVC.
Basically the Roles are static (Administrator, Client, Technician) but what they can do in each section is configurable, Add/Edit/Delete/View.
For example you are able to configure a Client's security to specific areas like User Accounts.
A user can be a combination of any roles, so it is possible to be a Client and a Technician and have the combined privlages of both users.
What would be a suitable way to go about doing this in ASP.NET MVC?
This is how we did it
public enum YourUserRoles
{
None = 0,
Admin = 1,
Consumer = 2
}
public class YourAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
private readonly YourUserRoles[] _acceptedRoles;
public YourAuthorizeAttribute(params VoicelrUserRoles[] acceptedroles)
{
_acceptedRoles = acceptedroles;
}
public YourAuthorizeAttribute(params bool[] allowAll)
{
if (allowAll[0])
_acceptedRoles = new[] { VoicelrUserRoles.Admin, VoicelrUserRoles.Consumer };
}
public void OnAuthorization(AuthorizationContext filterContext)
{
if (SessionHelper.UserInSession == null)//user not logged in
{
string retUrl = filterContext.HttpContext.Request.RawUrl;
FormsAuthentication.SignOut();
filterContext.Result =
new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary {{ "controller", "home" },
{ "action", "index" },
{ "returnUrl", retUrl } });//send the user to login page with return url
return;
}
if (!_acceptedRoles.Any(acceptedRole => SessionHelper.UserInSession.Roles.Any(currentRole => acceptedRole == currentRole)))
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/Error.cshtml"
};
}
}
}
[YourAuthorize(YourUserRoles.Client )]
public ActionResult Whatever()
{
....
}
I would provide you this resolution:
Data Base
Users ([PK]Id, Name, etc.)
Roles ([PK]Id, Name, Description, etc.)
UserRoles ([PK]UserId, [PK] RoleId)
Users table stores information about users, like their names, emails, etc. Roles stores information about rolesm like its name, description, etc. UserRoles is just look-up table which you can use to tie specific user to specific role.
Code
In order to let your code to work with these tables, you can add your custom role provider where you will have enough facility with 2 methods that will be: public override string[] GetRolesForUser(string username) and public override bool IsUserInRole(string username, string roleName).
Usage
When you'll complete, you simply can use Authorize attributes [Authorize(Roles = "Administrators")] to check if user has access to specific action or controller or you can use Razor verification in order to show/hide some html based on users role #User.IsInRole("Administrator")
Please check following links for more info
Custom Role Provider for MVC
Custom Membership and Role provider in ASP.NET MVC 4
Initially you can check the user exist in how many roles?
Create a view to show the Roles and write the message "please select a role to proceed "
After choose the user type proceed the user as the selection.

Custom user Login in ASP.NET MVC 4

I'm developing an ASP.NET MVC 4 Application, wherein I need every user to be redirected to his custom page upon login. The users are obtained from the UserProfile class which I have refactored into a separate class file. How do I modify the Redirect To method in the Login (post) Action in a ASP.NET MVC 4 Internet Project to get this functionality? Further how do I pass this data to a User controller that can display information related to this specific user.
I'm using simple Membership as it comes out of the box in an internet application template in ASP.NET MVC 4.
I'm guessing you're talking about this piece of code in the MVC4 template? I'm doing something very similar - upon login, I redirect the user to a page called Index.cshtml listed under the Account controller :
[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, model.RememberMe))
{
return RedirectToAction("Index", "Account");
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError(string.Empty, LocalizedText.Account_Invalid_User_Or_Password);
return View(model);
}
For user specific data, why not just extend the UsersContext.cs class in the Classes folder, then use WebSecurity.CurrentUserId to retrieve the information that pertains to that user?
Extended UsersContext class :
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Name { get; set; }
public bool IsPromotional { get; set; }
public bool IsAllowShare { get; set; }
}
This is the Index() action on the Account controller that they get redirected to upon login. Here I just call the users context, new up an AccountModel that's bound to the Index.cshtml page, set those attributes in the model, then return the View with the model we've built :
public ActionResult Index()
{
//New up the account model
var account = new AccountModel();
try
{
//Get the users context
var CurrentUserId = WebSecurity.CurrentUserId;
var context = new UsersContext();
var thisUser = context.UserProfiles.First(p => p.UserId == CurrentUserId);
//Set the name
account.Name = thisUser.Name;
//Set the user specific settings
account.IsAllowShare = thisUser.IsAllowShare;
account.IsPromotional = thisUser.IsPromotional;
}
catch (Exception exception)
{
_logger.Error(exception, "Error building Account Model");
}
return View(account);
}
It may not be exactly what you're looking for, but that should get you moving in the right direction.

How to tap into the automatic repeated login?

I am making an ASP.Net MVC3 application. I use for now the built in Authentication code that comes with a Visual Studio 2010 project. The problem is dat I need to retrieve the logged in user's database ID as soon as he has logged in. I do that now by adding code to the Login Action of the Account controller that retrieves the ID from the database by looking it up by username. This works for new logins, but not for "remembered" ones. On restarting the application the last user is automatically logged in again, but the Login code is not fired, so I do not get the database ID.
How can I solve this?
EDIT:
I tried to implement Daniel's solutions which looks promising and I came up with this code. It nevers gets called though! Where have I gone wrong?
Global.asax.cs:
protected void Application_Start()
{
Database.SetInitializer<StandInContext>(new StandInInitializer());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
this.AuthenticateRequest +=
new EventHandler(MvcApplication_AuthenticateRequest);
}
void MvcApplication_AuthenticateRequest(object sender, EventArgs e)
{
if(Request.IsAuthenticated)
{
using (var db = new StandInContext())
{
var authenticatedUser = db.AuthenticatedUsers.SingleOrDefault(
user => user.Username == User.Identity.Name);
if (authenticatedUser == null)
return;
var person = db.Persons.Find(authenticatedUser.PersonID);
if (person == null)
return;
Context.User = new CustomPrincipal(
User.Identity, new string[] { "user" })
{
Fullname = person.FullName,
PersonID = person.PersonID,
};
}
}
}
You can use the AuthenticateRequest event in your Global.asax.cs:
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
{
// retrieve user from repository
var user = _membershipService.GetUserByName(User.Identity.Name);
// do other stuff
}
}
Update:
Now that I see what you're trying to do a little clearer, I would recommend against using sessions in this particular case. One reason is that Session requires a reference to System.Web, which you don't have access to from some places, like a business logic layer in a separate class library. IPrincipal, on the other hand, exists for this very reason.
If you need to store more user information than what IPrincioal provides, you simply implement it and add your own properties to it. Easier yet, you can just derive from GenericPrincipal, which implements IPrincipal and adds some basic role checking functionality:
CustomPrincipal.cs
public class CustomPrincipal : GenericPrincipal
{
public CustomPrincipal(IIdentity identity, string[] roles)
: base(identity, roles) { }
public Guid Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
...
}
So then you replace the default principal with your own in AuthenticateRequest, as before:
Global.asax.cs
protected void Application_AuthenticateRequest()
{
if (Request.IsAuthenticated)
Context.User = _securityService.GetCustomPrincipal(User.Identity.Name);
}
And that is it. The greatest advantage you get is that you automatically get access to your user data from literally everywhere, without having to stick a userId parameter into all your methods. All you need to do is cast the current principal back to CustomPrincipal, and access your data like so:
From your razor views:
<p>Hello, #((CustomPrincipal)User).FirstName!</p>
From your controllers:
var firstName = ((CustomPrincipal)User).FirstName;
From a business logic layer in another assembly:
var firstName = ((CustomPrincipal)Thread.CurrentPrincipal).FirstName;
To keep things DRY, you could pack this into an extension method and hang it off IPrincipal, like so:
public static class PrincipalExtensions
{
public static string GetFirstName(this IPrincipal principal)
{
var customPrincipal = principal as CustomPrincipal;
return customPrincipal != null ? customPrincipal.FirstName : "";
}
}
And then you would just do #User.GetFirstName(), var userName = User.GetFirstName(), Thread.CurrentPrincipal.GetFirstName(), etc.
Hope this helps.
I wasn´t thinking clear. I was trying to store the userinfo in the Session object, while it available through the User object. Sorry to have wasted your time.

Using NerdDinner as base reference, how to perform data access tasks in controller?

I am trying to follow the Nerd Dinner MVC application as a base to learn the correct way to develop MVC applications.
I have created Interfaces and Repositories as the reference code suggests and am using Entity Framework for data access.
If I want to insert data when a user registers into a table [dbo].[Users], I do not have a controller for Users, how do I do it?
AccountController.cs
[HandleError]
public class AccountController : BaseController
{
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Password, model.Email);
if (createStatus == MembershipCreateStatus.Success)
{
// TODO: Enter record into [Users] get reference to [Aspnet_UserId]
// How do I do this??
//FormsService.SignIn(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);
}
}
If I create a UsersController to display views based on the Users table, how would I then add a new record when the user is registering?
I have a separate table [Users] that I wish to populate when a new user registers adding the [Aspnet_UserId] Guid.
You don't need to have a controller for each table in your database. In the code above, the MembershipService is the code that is actually creating the record (via the Repository for users).
The controllers should represent various areas and groups of functionality your website provides. While in many cases, you might have a controller with View, Create, and Update actions that do relate to a specific entity, that does relate to a specific database table, that isn't and shouldn't always be the case.
If it makes sense to have a UsersController because you want to view a list of users, or a specific users profile, that's fine, but the form for creating a user doesn't have to be a part of that controller. Having it be a part of a membership, or admin, or account, or registration controller is ok too.
Update
I'll try to provide you sample code of how I would expect the code to look. But you might have something else in mind, which is fine too, there's no true single way to do these things.
In your code above, I'm not sure what your MembershipService class is doing. It appears there is a static method on it that does something related to User Creation. I would expect that your MembershipService class should be calling your UserRepository to actually do the user creation. But you probably wouldn't want a static class/method for this.
public class MembershipCreationResult
{
public User Member { get; private set; }
public MembershipCreateStatus MembershipCreateStatus { get; private set; }
public MembershipCreationResult(User user, MembershipCreateStatus status)
{
Member = user;
MembershipCreateStatus = status;
}
public bool Success
{
get { return MembershipCreateStatus == MembershipCreateStatus.Success; }
}
}
public class MembershipService
{
public IUserRepository { get; private set; }
public MembershipService(IUserRepository userRepository)
{
UserRepository = userRepository;
}
public MembershipCreateResult CreateUser(string name, string password, string email)
{
User member = UserRepository.Create(name, password, email);
bool status = member != null ? MembershipCreateStatus.Success : MembershipCreateStatus.Failure;
return new MembershipCreationResult(status, member)
}
}
I haven't taken a very close look at the NerdDinner sample, and I haven't used the ASP.NET membership provider, but the concept I have outlined above should work. If MembershipService does something way different from what I have outlined, then you could create a new service to wrap the functionality and leave the existing MembershipService alone.

Resources