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.
Related
If I adopted the last scenario in this thesis :
Then my main layers will be like that:
UI Service (MVC application)
Business Layer
Security Service (used as a wrapper class library for MS identity
framework)
Aspects which use the previous security service to Authorize the
business layer methods.
public class EditEmployeeData : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Employee emp = (Employee)args.Instance;
((System.Security.Claims.ClaimsIdentity)System.Web.HttpContext.Current.User.Identity).HasClaim("Employee", "EditName");
}
}
I want to set the current user in runtime.
How to access the current user to authorize him on a specific
functionality in business layer?
Should the authorization be more near to the UI to disable/hide functionality and to prevent calling not allowed action methods ?(In the preferred scenario there's not any interaction between the security layer and the UI !!)
Update
Please see this answer about using claims...
In a controller, you can get the current user like this:
using Microsoft.AspNet.Identity.Owin;
public class MyController : Controller
{
// this code will return 0 if user is not authenticated
protected long GetUserId()
{
// note: I have changed the default UserId type from Guid to long
return User.Identity.GetUserId<long>();
/*
* use this if you are using Guid UserIds (which is the default)
* return User.Identity.GetUserId();
*/
}
See this, if you want to know how to change type of UserId.
If you have access to HttpContext, you can get the user like this:
// note that I have changed UserId from Guid to long
HttpContext.Current.User.Identity.GetUserId<long>()
If you want to get ApplicationUser use this (more info here):
// this is how you get user manager from OwinContext
var userManager = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
// Get ApplicationUser from UserManager
ApplicationUser user = UserManager.FindById(User.Identity.GetUserId());
How to access the current user to authorize him on a specific
functionality in business layer?
If you need to access current user in a service, you can pass it through or you can inject it. Using ninject, this is how you can inject UserId into a service:
kernel.Bind<MyService>().ToConstructor(ctorArg => new MyService(
HttpContext.Current.User.Identity.GetUserId<long>()).InRequestScope();
And this is how MyService class looks like:
public class MyService
{
private readonly long _userId;
public MyService(long userId)
{
// this service always has access to current user (if logged in)
_userId = userId;
}
// more code...
I am not sure what is the process of your authorization... ASP.NET Identity, already implements authorization task for you. This is implemented in ApplicationUserManager and ApplicationSignInManager which comes with ASP.NET MVC default template. You can use [Authorize] attribute on your action/class to prevent unauthorized access:
[Authorize] // <-- restricts all action methods of the class, unless marked [AllowAnonymous]
public class MyController : Controller
{
[HttpPost]
[Authorize] // <-- restricts this particular action method
public ActionResult MyAction(long id)
{
// do some action which requires authorization
}
Regarding DDD layers, have a look at this this link which explains services which belong to each layer.
How to access the current user to authorize him on a specific functionality in business layer?
To access user information on the business layer, you can type an interface named ICurrentUser
namespace AOPSample
{
public interface ICurrentUser
{
User GetCurrentUser();
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Role { get; set; }
}
}
The CurrentUser class must be able to read the information of the user from a common location. HttpContext is available for this.
Let's write a helper class for this.
using System.Web;
namespace AOPSample
{
public class ContextHelper
{
public T Get<T>()
{
T local = default(T);
string key = typeof(T).GUID.ToString();
if (HttpContext.Current.Items.Contains(key))
{
local = (T)HttpContext.Current.Items[key];
}
return local;
}
public T Get<T>(string key)
{
T local = default(T);
if (HttpContext.Current.Items.Contains(key))
{
local = (T)HttpContext.Current.Items[key];
}
return local;
}
public void Set<T>(T value)
{
string str = typeof(T).GUID.ToString();
HttpContext.Current.Items[str] = value;
}
public void Set<T>(T value, string key)
{
HttpContext.Current.Items[key] = value;
}
}
}
Our CurrentUser class will return user information using your helper class
namespace AOPSample
{
public class CurrentUser : ICurrentUser
{
public User GetCurrentUser()
{
return new ContextHelper().Get<User>();
}
}
}
now user information write to HttpContext with ContextHelper class and for this use correct location interceptor class
public class EditEmployeeData : OnMethodBoundaryAspect
{
public override void OnEntry(MethodExecutionArgs args)
{
Employee emp = (Employee)args.Instance;
((System.Security.Claims.ClaimsIdentity)System.Web.HttpContext.Current.User.Identity).HasClaim("Employee", "EditName");
new ContextHelper().Set<User>(new User
{
});
}
}
You can access user information from the domain layer with ICurrentUser. HttpContext is unique for every request and response
Should the authorization be more near to the UI to disable/hide functionality and to prevent calling not allowed action methods ?(In the preferred scenario there's not any interaction between the security layer and the UI !!)
It's your choice
In my opinion, you can take user privileges and log them with cache and use them for client side actions, but according to the technology you use for server side, you can store user information for each request in a similar way. For example; The correct location to store the OperationContext for wcf.
If you use ASP.NET Identity, you can try the following approach in order to get current User:
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
//If you use int instead of string for primary key, use this:
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(Convert.ToInt32(System.Web.HttpContext.Current.User.Identity.GetUserId()));
Hope this helps...
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
{ }
In ASP.NET MVC Identity,the relations data for Users and Roles is saved in AspNetUserRoles table, this table has two field:UserId,RoleId, but i want to add other fields to this table, such as department field.
So if an user logins in different departments,he will have different roles.
Anyone knows how to do it? Thanks in advance!
I Would Suggest you investigate ASPNet User Claims. You can assign different claims to a user with the identity manager, and based on the claim type of the user you will allow him access or not. Create a custom Claims Attribute which will be placed on top of the various controller to authenticate the user. this must be implemented based on your needs. the custom attribute will then fire before the controller gets executed and if the uses is allowed he will pass. else return to error page of you choice.
Sample Attribute usage
[ClaimsAuthorize(ClaimsData.EditAddress)]
public ActionResult CitiesPartial()
Attribute Authentication
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
private readonly string _claimType;
public ClaimsAuthorizeAttribute(string type)
{
_claimType = type;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var user = (ClaimsPrincipal)HttpContext.Current.User;
if (user.HasClaim(_claimType, "True"))
{
base.OnAuthorization(filterContext);
}
else
{
HandleUnauthorizedRequest(filterContext, _claimType + " Not Allowed ");
}
}
protected void HandleUnauthorizedRequest(AuthorizationContext filterContext, string message)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "action", "ClaimNotAuthorized" },
{ "controller", "Home" },
{"errorMessage", message }
});
}
public static bool AuthorizedFor(string claimType)
{
var user = (ClaimsPrincipal)HttpContext.Current.User;
return user.HasClaim(claimType, "True");
}
}
hope this helps.
I have a windows authentication MVC app that needs the username to do a lookup to determine if links are visible and set authorization. Note: I do visibility/Authorization with roles as well.
I need the username so I am currently doing it in OnAuthentification (not sure if this is the right place). I am splicing the username down to put it on the main page and say welcome, User. (presentation purposes)
[Authorize]
public abstract class ApplicationController : Controller
{
public static bool IsApprover;
protected override void OnAuthentication(AuthenticationContext filterContext)
{
base.OnAuthentication(filterContext);
if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated == true)
{
string userName = User.Identity.Name.Remove(0, 16).Replace('.', ' ').ToLower();
HttpContext.Application["UserName"] = TitleCase(userName, "Nothing");
//Initialize Values
HttpContext.Application["IsApprover"] = false; //used for link visibility
IsApprover = false; //used for Authorization
//do db lookup and set IsApprover values
}
}
}
So, I set the values above. I am not including the entity framework code just to be brief. The above works fine and every controller inherits from ApplicationController.
I also have
public class CustomAuthorizationValue : AuthorizeAttribute
{
private bool localIsAllowed;
public CustomAuthorizationValue(bool isAllowed)
{
localIsAllowed = isAllowed;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.Request.IsLocal)
{
var authorized = base.AuthorizeCore(httpContext);
if (!authorized)
{
// The user is not authorized => no need to go any further
return false;
}
return localIsAllowed;
}
return false;
}
}
For Authorization I use:
[CustomAuthorizationValue(IsApprover)]
public ActionResult Approve()
{
//code
}
For Visibility in Razor I use
#if((bool)#HttpContext.Current.Application["IsApprover"] == true)
{
<li>Approve (#HttpContext.Current.Application["ApproveCount"])</li>
}
This works fine but I have 2 different variables to use,
one for visibility (HttpContext.Current.Application["IsApprover"])
and
one for Authorization (IsApprover).
Is there a more elegant solution?
Is there another place to put the code rather than override void OnAuthentication?
Is there a way I can just set 1 variable for visibility and Authorization rather than having 2?
Is this the best practice or am I way off?
The above works fine and every controller inherits from
ApplicationController.
Hmmmm. You are storing user specific information information in the wrong scope:
HttpContext.Application["UserName"] = TitleCase(userName, "Nothing");
HttpContext.Application["IsApprover"] = false;
In ASP.NET, the Application scope is shared among ALL users of your website. So you have a concurrency issue here.
You should use the HTTP Context scope instead:
HttpContext.Items["UserName"] = TitleCase(userName, "Nothing");
HttpContext.Items["IsApprover"] = false;
Is there a more elegant solution?
You could use a view model:
public class MyViewModel
{
public string UserName { get; set; }
public bool IsApprover { get; set; }
}
and then have a couple of extension methods to work more easily:
public static class HttpContextExtensions
{
private const string MyViewModelKey = "__MyViewModel__";
public static MyViewModel GetMyViewModel(this HttpContextBase context)
{
return (MyViewModel)context.Items[MyViewModelKey];
}
public static void SetMyViewModel(this HttpContextBase context, MyViewModel model)
{
context.Items[MyViewModelKey] = model;
}
}
and then use those extension methods:
if (filterContext.RequestContext.HttpContext.User.Identity.IsAuthenticated)
{
string userName = User.Identity.Name.Remove(0, 16).Replace('.', ' ').ToLower();
bool isApprover = ... do db lookup and set IsApprover value
var model = new MyViewModel
{
UserName = TitleCase(userName, "Nothing"),
IsApprover = isApprover,
}
this.HttpContext.SetMyViewModel(model);
}
and in your view:
#if(HttpContext.GetMyViewModel().IsApprover)
{
<li>
<a href="#Url.Action("Approve", "Approve")">
Approve (#HttpContext.Current.Application["ApproveCount"])
</a>
</li>
}
NOTE: In this anchor text once again you seem to be using the Application scope to store user specific information such as ApproveCount which we discussed earlier.
Is this the best practice or am I way off?
Well, I would probably use claims based authentication and store this information (IsApprover, ...) as claims in the current user.
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.