Hi guys so I'm using a custom membership provider and custom role provider. And it is logging in using these correctly. I also implemented my own Membership user object so that I can gain access to other user information, and I don't need to load all the data everytime I change page, but I currently cannot get this to work properly. Below is my user object:
public class User : MembershipUser
{
[Required(ErrorMessage = "Username cannot be blank")]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required(ErrorMessage = "Password cannot be blank")]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[Display(Name = "User ID")]
public long UserID { get; set; }
[Display(Name = "Family Name")]
[StringLength(50, ErrorMessage = "Family name cannot be longer than 50 characters")]
public string FamilyName { get; set; }
[Display(Name = "Given Name")]
[StringLength(50, ErrorMessage = "Given name cannot be longer than 50 characters")]
public string GivenName { get; set; }
public Company Company { get; set; }
public virtual IIdentity Identity { get; set; }
}
And when the user logs in I call the following login method:
[AllowAnonymous]
[HttpPost]
public ActionResult Login(User model, string returnUrl)
{
FormsAuthentication.SignOut();
if(Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, true);
return RedirectToAction("Index", "");
}
ViewBag.Message = "Failed to login";
return View();
}
But when I call HttpContext.User in index it just contains name/ID, not the rest of my user object. Do I need to create a custom FormAuthentication object? Or is it standard process to store all this user information inside the HttpContext.Session object? Or get my user to extend the System.Security.Principle.IPrinciple? Or even in the Controller.TempData? Or somewhere else I'm not familiar with. I dont want to have to hit the database everytime to load the user data.
Sorry if these are obvious questions I'm fairly new to web development and not sure what the generic way of doing these things are. Trying to use the in build Authorize attributes.
I did it by implementing my own identity. That way it's easy to add as many properties as I need. Below is a code example with custom property friendlyName
public class Identity : IIdentity
{
public Identity(int id, string name, string friendlyName, string roles)
{
this.ID = id;
this.Name = name;
this.FriendlyName = friendlyName;
this.Roles = roles;
}
public Identity(string name, string data)
{
if (string.IsNullOrWhiteSpace(data))
throw new ArgumentException();
string[] values = data.Split('|');
if (values.Length != 3)
throw new ArgumentException();
this.Name = name;
this.ID = Convert.ToInt32(values[0]);
this.FriendlyName = values[1];
Roles = values[2];
}
public string AuthenticationType
{
get { return "Custom"; }
}
public bool IsAuthenticated
{
get { return true; }
}
public override string ToString()
{
return FriendlyName;
}
public string GetUserData()
{
return string.Format("{0}|{1}|{2}", ID, FriendlyName, Roles);
}
public int ID { get; private set; }
public string Name { get; private set; }
public string FriendlyName { get; private set; }
public string Roles { get; private set; }
}
//in controller on login action:
Identity id = new Identity(user.ID, user.Username, "some friendly name", user.Roles);
DateTime expire = DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(id.ID, user.Username, DateTime.Now, expire, false, id.GetUserData());
string hashTicket = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashTicket);
HttpContext.Response.Cookies.Add(cookie);
In global.asax you have:
public override void Init()
{
base.Init();
PostAuthenticateRequest += new EventHandler(MvcApplication_PostAuthenticateRequest);
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket == null || authTicket.Expired)
return;
Identity id = new Identity(authTicket.Name, authTicket.UserData);
Principal user = new Principal(id);
Context.User = user;
Thread.CurrentPrincipal = user;
}
}
Related
Again...
I am doing a MVC with EF5 App. I have a Users Entity, that EF bind with Users table in Database... Looks like this.
public partial class Users
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
public int ProcessState_id { get; set; }
public string Sex { get; set; }
[Required,Range(1, int.MaxValue, ErrorMessage = "El País es Obligatorio")]
public int Country_id { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public System.DateTime CreationDate { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[Required]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Password cannot be longer than 9 characters.")]
public string Password { get; set; }
public string Url { get; set; }
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
public virtual Countries Countries { get; set; }
public virtual ProcessStates ProcessStates { get; set; }
public virtual States States { get; set; }
[NotMapped] // Does not effect with your database
[RegularExpression(#"^.{5,}$", ErrorMessage = "Minimum 3 characters required")]
[StringLength(9, MinimumLength = 3, ErrorMessage = "Confirm Password cannot be longer than 9 characters.")]
[Compare("Password")]
public virtual string ConfirmPassword { get; set; }
}
I have a Model Class that i use it in my Create View....
public class UserViewModel
{
public Users user { get; set; }
public IList<SelectListItem> AvailableCountries { get; set; }
}
My Create Method in the Controller gets a UserViewModel instance...
My Create Method looks like this.
public async Task<ActionResult> Create(UserViewModel model, System.Web.HttpPostedFileBase image = null)
{
try
{
if (ModelState.IsValid)
{
model.user.ProcessState_id = Security.WAITING;
model.user.Rol_id = Security.ROL_PUBLIC;
model.user.CreationDate = DateTime.Now;
model.user.IP = Request.UserHostAddress;
model.user.Url = UserValidation.EncriptacionURL(model.user.Email);
if (image != null)
{
// product.ImageMimeType = image.ContentType;
model.user.Picture= new byte[image.ContentLength];
image.InputStream.Read(model.user.Picture, 0, image.ContentLength);
}
_db.Users.Add(model.user);
_db.SaveChanges();
return RedirectToAction("Create");
}
model.AvailableCountries = GetCountries();
return View(model);
}
catch (RetryLimitExceededException /* dex */)
{
}
return View(model);
}
So far so good.
For my Edit View, i need less properties from User class, so I have a new class with the properties I need. This class is called UserEditView.
public class UserEditView
{
public long User_id { get; set; }
[Required]
[StringLength(30, ErrorMessage = "LastName cannot be longer than 30 characters.")]
public string LastName { get; set; }
[Required]
[StringLength(30, ErrorMessage = "Name cannot be longer than 30 characters.")]
public string Name { get; set; }
[Required, Range(1, int.MaxValue, ErrorMessage = "El País es Obligatorio")]
public int Country_id { get; set; }
[Required]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string Email { get; set; }
public Nullable<System.DateTime> UpDateTime { get; set; }
public byte[] Picture { get; set; }
public string CodArea { get; set; }
public string PhoneNumber { get; set; }
public virtual Countries Countries { get; set; }
}
I also create a new Model for Edit View, called UserEditViewModel and looks like this.
public class UserEditViewModel
{
public UserEditView user { get; set; }
public IList<SelectListItem> AvailableCountries { get; set; }
}
On my Edit method, I use Mapper to bind User entity with UserEditView
public ViewResult Edit(int User_id=3)
{
Users users = _db.Users
.FirstOrDefault(p => p.User_id == User_id);
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
});
IMapper mapper = config.CreateMapper();
UserEditView userEditView = mapper.Map<Users, UserEditView>(users);
var model = new UserEditViewModel
{
user = userEditView,
AvailableCountries = GetCountries(),
};
return View(model);
}
My problem arise when I want to Update the User table.
The Edit method gets UserEditViewModel instance.
public async Task<ActionResult> Edit(UserEditViewModel model, System.Web.HttpPostedFileBase image = null)
{
try
{
if (ModelState.IsValid)
{}
}
}
UserEditViewModel has an instance of UserEditView but I need an instance of Users in order to EF updates Users Table.
I need to Map again?
How can I get a Users Instance?
I add the following Class
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
cfg.CreateMap<UserEditView, Users>();
});
IMapper mapper = config.CreateMapper();
}
And I add in my Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AutoMapperBootStrapper.BootStrap();
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
then in the controller... i do
public ViewResult Edit(int User_id=3)
{
Users users = _db.Users.FirstOrDefault(p => p.User_id == User_id);
UserEditView userEditView = Mapper.Map<Users, UserEditView>(users);
}
But Mapper.Map have an error... it says Mapper is not instantiated.
the problem is because I defined more than one Mapper. If i define just one, it Works fine...
I need to Map again? How can I get a Users Instance?
You could get the User model from your database using the id and then map the properties that you need to be updated from the view model:
[HttpPost]
public ActionResult Edit(UserEditViewModel model, HttpPostedFileBase image = null)
{
if (!ModelState.IsValid)
{
// Validation failed => redisplay the Edit form so that the
// user can correct the errors
return View(model);
}
var user = _db.Users.FirstOrDefault(p => p.User_id == model.user.User_id);
if (user == null)
{
// no user with the specified id has been found in the database =>
// there's nothing to update
return NotFound();
}
// This will map only the properties of the user object that
// are part of the view model
Mapper.Map<Users, UserEditView>(model.user, user);
// at this stage you could manually update some properties that
// have not been mapped such as the uploaded image
// finally persist the changes to the database
_db.SaveChanges();
// redirect to some other action to show the updated users
return RedirectToAction("users");
}
Also the code you have shown in your question:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Users, UserEditView>();
});
IMapper mapper = config.CreateMapper();
This is absolutely NOT something that you should be doing inside a controller action. AutoMapper mappings should be configured only once per application lifetime, ideally when your application starts, i.e. for a web application that would be Application_Start in Global.asax. In a controller action you should only use the already configured mappings. I strongly recommend you going through the AutoMapper's documentation for getting better understanding of how to use this framework.
Quote from the documentation:
Where do I configure AutoMapper?
If you're using the static Mapper method, configuration should only
happen once per AppDomain. That means the best place to put the
configuration code is in application startup, such as the Global.asax
file for ASP.NET applications. Typically, the configuration
bootstrapper class is in its own class, and this bootstrapper class is
called from the startup method. The bootstrapper class should call
Mapper.Initialize to configure the type maps.
New to MVC. Using Entity Framework and database first.
Problem
-Register page has fields for username, password, confirm password, and e-mail with validators.
-Login page has username and password fields also with validators. Login page asks for confirm password and e-mail when it shouldn't.
Goal
-Fix this, and maybe learn some proper way of using models in the meantime. I have read many articles and seen many videos but a lot use code first, which I am not using. Very confusing to me.
CODE
My User database doesn't have a Confirm Password field so I made a model...
namespace StoreFront.Models
{
[MetadataType(typeof(RegisterViewModel))]
public partial class User
{
[DataType(DataType.Password)]
public string ConfirmPassword { get; set; }
}
}
My CustomerBaseViewModel has the LoginViewModel and RegisterViewModel that my views should be using...
public class LoginViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
}
public class RegisterViewModel
{
public int UserID { get; set; }
[DisplayName("Username")]
[Required(ErrorMessage = "Username is required")]
public string UserName { get; set; }
[DisplayName("Password")]
[DataType(DataType.Password)]
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[DisplayName("Confirm Password")]
[DataType(DataType.Password)]
[Compare("Password", ErrorMessage = "Passwords must match")]
public string ConfirmPassword { get; set; }
[DisplayName("Email")]
[Required(ErrorMessage = "Email is required")]
[RegularExpression(#"^([a-zA-Z0-9_\-\.]+)#([a-zA-Z0-9_\-\.]+)\.([a-zA-Z]{2,5})$",
ErrorMessage = "Please enter a valid email")]
public string EmailAddress { get; set; }
}
I have two separate controllers...one for login and one for register. Not sure if you need to look at it but I added anyway
public class LoginController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(User user)
{
using (StoreFrontContext db = new StoreFrontContext())
{
if (!ModelState.IsValid)
{
return View();
}
else
{
var usr = db.Users.Where(u => u.UserName.ToLower() == user.UserName.ToLower() && u.Password == user.Password).FirstOrDefault();
if (usr != null)
{
Session["Username"] = usr.UserName.ToString();
Session["UserID"] = usr.UserName.ToString();
}
else
{
ModelState.AddModelError("", "Username or Password is incorrect.");
}
}
}
return View();
}
}
public class RegisterController : Controller
{
StoreFrontContext db = new StoreFrontContext();
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(User account)
{
if (ModelState.IsValid)
{
using (db)
{
bool duplicate = true;
foreach (var name in db.Users)
{
if (account.UserName.ToLower() == name.UserName.ToString().ToLower())
{
ModelState.AddModelError("", "Username already exists in database!");
duplicate = true;
break;
}
else
{
duplicate = false;
}
}
if (!duplicate)
{
account.IsAdmin = false;
account.DateCreated = DateTime.Now;
db.Users.Add(account);
db.SaveChanges();
ModelState.Clear();
}
}
}
return View();
}
}
Login Index() View uses #model StoreFront.Models.LoginViewModel and Register Index() View uses #model StoreFront.Models.RegisterViewModel
I can't seem to figure out what is wrong, but I believe it's what is being passed through the View or the controller. I'm still not very familiar with the Model part of MVC yet..been very struggling on that, so any pointers on that would also help. Any help would be strongly appreciated!!
I'm trying to modify the default MVC project so that instead of showing a username, I can display their full name. Eg, the default app shows
Hello <username>! Log off
I added a new property FullName to the ApplicationUser class. The code that shows the name currently is:
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues:=Nothing, htmlAttributes:=New With {.title = "Manage"})
So how can I get lookup that value from the ApplicationUser class and display it here? Additionally, is there a way to cache this? It seems like a waste to perform a lookup for every request.
I also might want to show their email address instead, so I definitely need to use a new property.
I generally like to serialize a user object in the FormsAuthentication cookie when they login and then create a class inheriting from IPrincipal so that my views can read the de-serialized object:
public interface IUserPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Username { get; set; }
}
public class UserPrincipal : IUserPrincipal
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public IIdentity Identity { get; private set; }
public UserPrincipal(string Username)
{
this.Identity = new GenericIdentity(Username);
}
}
public class UserPrincipalPoco
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
}
and then when authenticating:
public ActionResult Login(LoginViewModel vm, string ReturnUrl)
{
// Check for valid authentication
if (_authenticationService.Authenticate(vm.Username, vm.Password))
{
// Add forms authentication cookie
Response.Cookies.Add(GetFormsAuthenticationCookie(vm.Username));
// Redirect after authentication
}
// Failed authentication, redirect to unauthorized
}
private HttpCookie GetFormsAuthenticationCookie(string Username)
{
var user = _userService.GetUserByUsername(Username);
UserPrincipalPoco pocoModel = new UserPrincipalPoco();
pocoModel.Id = user.Id.Value;
pocoModel.FirstName = user.FirstName;
pocoModel.LastName = user.LastName;
pocoModel.Username = Username;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(pocoModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
Username,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
return new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
}
and then in global.asax.cs:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
UserPrincipalPoco serializeModel = serializer.Deserialize<UserPrincipalPoco>(authTicket.UserData);
UserPrincipal newUser = new UserPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
newUser.Username = serializeModel.Username;
HttpContext.Current.User = newUser;
}
}
Now you need to create a BaseViewPage that inherits from WebViewPage to tell your views to use your UserPrincipal object:
public abstract class BaseViewPage : WebViewPage
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
and in your Web.config tell your views to always use this BaseViewPage:
<pages pageBaseType="MyNameSpace.Views.BaseViewPage">
Now in my views I can access the user like:
#User.Username
or
#User.FirstName #User.LastName
few ways you can do it.
if the application is not too big, you may cache the user models that log in for a specific period of time so you can pull the entire user info based on the username.
you may save a list if info in User.Identity, including username, firstname, last name etc, separating them with a comma or etc.
a bad way: every time you need the extra info, hit the database and get them.
my opinion: cache the recent users who have been logged in for a specific amount of time. you will be able to create slick solutions using cashing. let me know if you need info.
i am using asp.net mvc 4 and entity framework 5 in a project. i have a base Entity that all entities derived from it:
public abstract class BaseEntity
{
[Required]
public virtual int Id { get; set; }
[Required]
public virtual DateTime CreatedOn { set; get; }
public virtual string CreatedBy { set; get; }
[Required]
public virtual DateTime ModifiedOn { set; get; }
public virtual string ModifiedBy { set; get; }
}
First the Account Entity is a class for application user:
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
public byte[] AvatarBinary { get; set; }
public string AvatarMimeType { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
Role of the User :
public class Role : BaseEntity
{
public string RoleName { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
each User can have multiple Role and vice versa:
public class AccountInRole : BaseEntity
{
public int AccountId { get; set; }
public int RoleId { get; set; }
public virtual Account Account { get; set; }
public virtual Role Role { get; set; }
}
when i want to give roles for an specific user, call GetRoles method in Accountrepository. this is implemented in this way:
public class AccountRepository : IAccountRepository
{
#region Properties
private CharityContext DataContext { get; set; }
public IQueryable<Account> Accounts
{
get { return DataContext.Accounts; }
}
#endregion
#region Ctors
public AccountRepository() : this(new CharityContext())
{
}
public AccountRepository(CharityContext db)
{
DataContext = db;
}
#endregion
#region Methods
public List<Role> GetRoles(string userName)
{
var acc = DataContext.Accounts;
var query = from u in DataContext.Accounts
from r in DataContext.Roles
from ur in DataContext.AccountInRoles
where ur.AccountId == u.Id && ur.RoleId == r.Id && u.UserName == userName
select r;
return query.ToList();
}
#endregion
}
in this method, an exception has thrown when the compiler want to run above LINQ query. this exception is:
StackOverflowException was unhandled
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
{Cannot evaluate expression because the current thread is in a stack overflow state.}
the GetRoles method are call two time :
one time from the Custom Authorize Attribute:
public class CustomAuthorize : AuthorizeAttribute
{
//private readonly IAccountRepository _accountRepository;
private string[] roles;
//public CustomAuthorize(params string[] roles)
//{
// this.roles = roles;
//}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (!httpContext.User.Identity.IsAuthenticated)
return false;
if (Roles == string.Empty)
return true;
var lstRoles = Roles.Split(',');
AccountRepository _accountRepository = new AccountRepository();
var userRoles = _accountRepository.GetRoles(httpContext.User.Identity.Name);
foreach (var role in lstRoles)
{
bool isFound = false;
foreach (var userRole in userRoles)
{
if (userRole.RoleName == role)
isFound = true;
}
if (!isFound) return false;
}
return true;
}
}
and second time from the Application_AuthenticateRequest method in the Global.asax.cs :
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cookie = FormsAuthentication.FormsCookieName;
HttpCookie httpCookie = Request.Cookies[cookie];
if (httpCookie == null) return;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(httpCookie.Value);
if(ticket == null || ticket.Expired) return;
FormsIdentity identity = new FormsIdentity(ticket);
var _accountRepository = new AccountRepository();
var roles = _accountRepository.GetRoles(identity.Name);
var principal = new CharityAccount(identity.Name, roles.Select(x => x.RoleName).ToArray());
Context.User = Thread.CurrentPrincipal = principal;
}
CharityAccount that ou can see it in above method is implemented in this way:
public class CharityAccount : IPrincipal
{
private string[] roles;
private IIdentity identity;
public IIdentity Identity
{
get { return identity; }
}
public bool IsInRole(string role)
{
return Array.IndexOf(roles, role) >= 0;
}
public CharityAccount(String name, String[] roles)
{
identity = new GenericIdentity(name, "Custom authentication");
this.roles = roles;
}
}
According to your idea, what is the problem?
regards
You have done few things which can lead you to troubles. The one I can see is the circular reference of Accounts, roles in AccountinRoles and vice versa.
I have simplified your code though it's not the best design(But I believe in keeping things simple and stupid). You can keep your virtual properties if you really mean what the virtual properties are for in entities.
This working and running fine.
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreatedOn { set; get; }
}
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class Role : BaseEntity
{
public string RoleName { get; set; }
}
public class AccountInRole
{
public int AccountId { get; set; }
public int RoleId { get; set; }
}
public class Operation
{
public List<Role> GetRoles()
{
List<Account> lstAccount = new List<Account>();
List<Role> lstRole = new List<Role>();
List<AccountInRole> lstAccountInRoles = new List<AccountInRole>();
Account ac1 = new Account
{
Id = 1,
UserName = "Jack",
Password = "somePassword2",
CreatedOn = DateTime.Now
};
Account ac2 = new Account
{
Id = 2,
UserName = "Sam",
Password = "somePassword1",
CreatedOn = DateTime.Now
};
lstAccount.Add(ac1);
lstAccount.Add(ac2);
Role r1 = new Role
{
Id = 1,
RoleName = "TestRole1",
CreatedOn = DateTime.Now
};
Role r2 = new Role
{
Id = 2,
RoleName = "TestRole2",
CreatedOn = DateTime.Now
};
lstRole.Add(r1);
lstRole.Add(r2);
AccountInRole acRole1 = new AccountInRole
{
AccountId = ac1.Id,
RoleId = r1.Id
};
AccountInRole acRole2 = new AccountInRole
{
AccountId = ac2.Id,
RoleId = r2.Id
};
lstAccountInRoles.Add(acRole1);
lstAccountInRoles.Add(acRole2);
string userName = "Sam";
// Query the data
var roles = from u in lstAccount
where u.UserName == userName
from acc in lstAccountInRoles
from r in lstRole
where acc.AccountId == u.Id
&& r.Id == acc.RoleId
select r;
return roles.ToList();
}
}
Trying to implement IPrincipal (ASP.NET MVC 3) and having problems:
my custom IPrincipal:
interface IDealsPrincipal: IPrincipal
{
int UserId { get; set; }
string Firstname { get; set; }
string Lastname { get; set; }
}
public class DealsPrincipal : IDealsPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public DealsPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
To serialize/deserialize i use the following class:
public class DealsPrincipalSerializeModel
{
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
The Application authenticate event is as follows (works fine!)
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
//get the forms ticket
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//instantiate a new Deserializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
//deserialize the model
DealsPrincipalSerializeModel serializeModel = serializer.Deserialize<DealsPrincipalSerializeModel>(authTicket.UserData);
//put the values of the deserialized model into the HttpContext
DealsPrincipal newUser = new DealsPrincipal(authTicket.Name); //this implements IPrincipal
newUser.UserId = serializeModel.UserId;
newUser.Firstname = serializeModel.Firstname;
newUser.Lastname = serializeModel.Lastname;
HttpContext.Current.User = newUser;
}
}
As you can see in the last statement the HttpContext gets assigned this new DealsPrincipal (which works fine).
The problem is that if want to access this User in a Controller(Action) i always get a base class object. If i cast the User as follows:
User as DealsPrincipal
to get for example the UserId (sample:
( User as DealsPrincipal).UserId
this is always null!!! Why? What am i missing?
I would need to investigate more to give you correct answer but look this part of the code and it could help you (part of the source of WindowsAuthenticationModule.cs)
void OnAuthenticate(WindowsAuthenticationEventArgs e) {
////////////////////////////////////////////////////////////
// If there are event handlers, invoke the handlers
if (_eventHandler != null)
_eventHandler(this, e);
if (e.Context.User == null)
{
if (e.User != null)
e.Context.User = e.User;
else if (e.Identity == _anonymousIdentity)
e.Context.SetPrincipalNoDemand(_anonymousPrincipal, false /*needToSetNativePrincipal*/);
else
e.Context.SetPrincipalNoDemand(new WindowsPrincipal(e.Identity), false /*needToSetNativePrincipal*/);
}
}
From this code I would suggest you to check if user is anonymous before assigning instance of your custom IPrincipal inmplementation. Also, not sure if this method is executed before or after "protected void Application_AuthenticateRequest". Will try to take more time to investigate this.
Also, please look at this article:
http://msdn.microsoft.com/en-us/library/ff649210.aspx