How to get user object and save it with model? - asp.net-mvc

I've created my incredibly simplistic model:
public class ImageModel
{
public int Id { get; set; }
public string FileName { get; set; }
}
And now I want to store the logged-in user with the record. Presumably, I would do this by adding another property:
public User User { get; set; }
Which Visual Studio is telling me is telling me is in
using System.Web.Providers.Entities;
Assuming that's the right User class that corresponds with the presently authenticated user, how do I save that with my model?
My Create action looks like this:
[HttpPost]
public ActionResult Create(ImageModel imagemodel)
{
if (ModelState.IsValid)
{
db.ImageModels.Add(imagemodel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(imagemodel);
}
I imagine I would want to add something like
imagemodel.User = User;
Just above db.ImageModels.Add, but those appear to be two different types. The User object in Controller appears to be an IPrincipal which really doesn't seem to hold much information at all.
How do I get the rest of the user data for the authenticated user? What do I need to assign imagemodel.User to? Is that even the way to do it, or do I need to explicitly tell it I just want to save the User ID (I'm assuming this much it could figure out) -- and if so, how do I keep a User object on my model such that it points to the real User object (not just an ID), and how do I get the User ID for the currently logged in user?

It is dependent upon what technology you are using to manage logons or sessions.
Inside the controller method, you probably just want to set your model property to the value in 'User.Identity.Name', which is a string value.
That assumes the user is logged in and that you have forms authentication configured. You've probably previously authenticated the user, and given them a token (basically just an encrypted cookie) containing the value of '.Name', via the FormsAuthentication.SetAuthCookie method.
So, to keep things very simple, your Image model should probably just have a Username string property. If you have access to the user identity table, you might want to store a reference to the related user instead.
public class ImageModel
{
public int Id { get; set; }
public string FileName { get; set; }
public string Username { get; set; }
}
Controller...
[HttpPost]
public ActionResult Create(ImageModel imagemodel)
{
if (ModelState.IsValid && User.Identity.IsAuthenticated)
{
imagemodel.Username = User.Identity.Name;
db.ImageModels.Add(imagemodel);
db.SaveChanges();
return RedirectToAction("Index");
}
The interfaces exposed without knowing what provider you are using are very minimal.
IPrinicipal User
bool IsInRole(string role)
IIdentity Identity
string AuthenticationType
bool IsAuthenticated
string Name
That's it.
Once you select a provider or decide to implement a custom one there's a whole range of SO articles that will fall into your lap.
You may also be better served looking for ASP.NET references than MVC references when researching this topic.

Related

Best way to check if record exists and use site wide

I am fairly new to c# and MVC but I am building an intranet app. Being on the internal network there is no need to sign in to use the app but I do have it connected to a database which has an 'Administration' table. In this table are the administrator's email addresses. I am also using System.DirectoryServices.AccountManagement and then UserPrincipal.Current.EmailAddress to get the users email address. What I would like to do is compare the UserPrincipal.Current.EmailAddress to the database table and if there is a match then set a boolean to TRUE that I can reference/call upon within my entire site.
I have a model matching the database tables and I can also query the database using a where statement to the value of UserPrincipal.Current.EmailAddress but only within a set method (ActionResult) and return the boolean value within a viewbag to that particular controller that is accessed by the related view only.
I would like to know what is best practice for setting up my site so that whichever page a users visits their email is compaired to the database and a boolean is set to true/false if they are/aren't in the database administrator table.
Edit: Would this be to create a base controller and then inherit it in all other controllers and within the base controller perform the database query - if so a little guidance would be greatly appricated
My current set up is an EmailEntityModel:
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class EmailEntities : DbContext
{
public EmailEntities()
: base("name=EmailEntities")
{
}
public virtual DbSet<Audience> Audiences { get; set; }
public virtual DbSet<CallToAction> CallToActions { get; set; }
public virtual DbSet<ColourScheme> ColourSchemes { get; set; }
public virtual DbSet<Email> Emails { get; set; }
public virtual DbSet<EmailType> EmailTypes { get; set; }
public virtual DbSet<Administrator> Administrators { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
Then I have an email Controller:
public class EmailsController : Controller
{
private EmailEntities db = new EmailEntities();
public ActionResult Index()
{
return View(db.Emails.ToList());
}
Can I use the EmailEntities to query the Administator DBset within my controller but can I use this elsewhere?
If I understood your question correctly, you want to query the DB on every request and compare the current user's email against the admin email. If that's the case, then you have many options.
If it was me, I would keep the Admin email in a constant/static variable (so I don't have to make the trip to the DB on every request):
public static class StaticCache
{
// static constructor would run only once, the first it is used
// this value is maintained for the entire life-time of the application
static StaticCache()
{
using (var context = MyApplicationDbContext.Create())
{
// get your admin email for the DB
AdminEmail = context.Email.Where(/*some admin flag == true*/).FirstOrDefault();
}
}
public static string AdminEmail;
public static bool IsAdminUser(string curEmail)
{
return string.Equal(curEmail, AdminEmail, StringComparison.InvariantCultureIgnoreCase);
}
}
That's it. Now you can call StaticCache.IsAminUser() anywhere in your program (even in your view). All you need is to pass the current email to the method.

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
{ }

Editing some properties of View Model in ASP.NET MVC

I'm using Entity Framework Database First approach. Let's say I have a model class called Product and that class has a NumberOfViews property. In the Edit page I pass an instance of the product class to the controller.
The problem is I can't add #Html.EditorFor(model => model.NumberOfViews) in the Edit page, because it's supposed that NumberOfViews is updated with every visit to the product page, and NOT by the website Admin.
And I can't add it as #Html.HiddenFor(model => model.NumberOfViews), because if the Admin Inspected the element, he can edit it manually.
Also If I try to programmatically set the value on the server-side (e.g., Product.NumberOfViews = db.Products.Find(Product.Id).NumberOfViews;), I get the following error:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
And if I don't add it to either the view or the controller, the value will be null, thus overriding any previous value.
So what should I do?
I have noticed a lot of people use the same model for their Entity Framework as they do for their MVC Controller. I generally discourage this practice. In my opinion, a database model is not the same as a view model.
Sometimes a view needs less information than what the database model is supplying. For example while modifying account password, view does not need first name, last name, or email address even though they may all reside in the same table.
Sometimes it needs information from more than one database table. For example if a user can store unlimited number of telephone numbers for their profile, then user information will be in user table and then contact information with be in contact table. However when modifying user profile, they may want to add/edit/delete one or more of their numbers, so the view needs all of the numbers along with first name, last name and email address.
This is what I would do in your case:
// This is your Entity Framework Model class
[Table("Product")]
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ProductId { get; set; }
public string Name { get; set; }
public int NumberOfPageViews { get; set; }
}
// This is the model you will use in your Edit action.
public class EditProductViewModel
{
public int ProductId { get; set; }
public string Name { get; set; }
}
public class ProductController : Controller
{
IProductService service;
//...
[HttpGet]
public ActionResult Edit(int productId)
{
var product = service.GetProduct(productId);
var model = new EditProductViewModel()
{
ProductId = product.ProductId,
Name = product.Name
};
return View(model);
}
[HttpPost]
public ActionResult Edit(EditProductViewModel model)
{
if (ModelState.IsValid)
{
var product = service.GetProduct(model.ProductId);
product.Name = model.Name;
service.Update(product);
}
// ...
}
}

Error with relationship

I have a User table and an Avatar table. One User can have many avatars (or null). But I need to mark which avatar is the current, so I have an Avatar_Id in User table that is the current avatar. And a ForeignKey User_Id in Avatar to tell me which User is the owner.
Trying to do that is generating me a lot of errors and headaches when I try to populate some data in order to test the relationship.
public class User
{
[Key]
public int Id { get; set; }
public Avatar Avatar { get; set; }
public virtual ICollection<Avatar> Avatars { get; set; }
}
public class Avatar
{
[Key, ForeignKey("User")]
public int Id { get; set; }
public User User { get; set; }
}
Test part:
var user = new User();
var avatar = new Avatar()
{
User = user
};
// user.Avatar = avatar; // <- this gives [a circular] error; without this I have null.
db.Users.Add(user);
db.Avatars.Add(avatar);
db.SaveChanges();
This is resulting me with Avatar_Id = NULL within User table, and User_Id = NULL in Avatar table. I expected these fields filled (well, Avatar_Id can be null).
Its better to make boolean field 'IsDefault' in table with avatar and check while add/update avatars that no more default avatars for this user. Also you can add same property in avatar class.
#Fabricio I can't test this code before post, but I'm pretty convinced it will work.
public class User
{
[Key]
public int UserId { get; set; }
public int AvatarId { get; set; }
[ForeignKey("AvatarId")]
public Avatar Avatar { get; set; }
public ICollection<Avatar> Avatars { get; set; }
}
public class Avatar
{
[Key]
public int AvatarId { get; set; }
[ForeignKey("User")]
public int UserId { get; set; }
public User User { get; set; }
}
The problem is when you put two foreign keys merge like one. Now you have a foreign key in Avatar table and other in User table, each one represents one mode of relationship.
The foreign key "AvatarId" represents a special form of foreign key, a unique + foreign key, (a second form to build the one to one relationship). You can read more about this in here: http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx
I've given this a bit of though because I modeled a similar case once and didn't mind to reevaluate the options.
Look closely at your premises:
One User can have many avatars (or null)
This short sentence implies that the one-to-one association User-Avatar must be optional both ways, because a User without Avatars can’t possibly refer to one its own avatars, and when a user has more than one avatar only one of them can refer to User as being the user's default. (They all refer to user as owner).
So you can only model it as a 0..1 – 0..1 association. So Avatar’s primary key can't be a foreign key to user. (It couldn't anyway, otherwise a user could only have one avatar).
Maybe this could have been done by Jonny Piazzi's model if this wouldn't throw the infamous "may cause cycles or multiple cascade paths" exception. Both user and Avatar refer to one another and you have to tell EF explicitly which of the FKs is not cascading. This can only be done by fluent mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasOptional(u => u.Avatar)
.WithOptionalDependent()
.Map(m => m.MapKey("AvatarId"))
.WillCascadeOnDelete(false);
...
}
This puts a nullable, non-cascading FK column AvatarId in User (that's why User is the dependent of Avatar).
Now your second issue, the chicken-eg problem when populating the model.
This can only be done when you call SaveChanges twice and wrap these calls in a transaction scope. For example:
using (var tran = new TransactionScope())
{
var user = new User();
var avatar = new Avatar();
user.Avatars = new HashSet<Avatar>();
user.Avatars.Add(avatar);
user.Avatars.Add(new Avatar());
user.Avatars.Add(new Avatar());
db.Users.Add(user);
db.SaveChanges();
user.Avatar = avatar; // set FK
db.SaveChanges();
tran.Complete();
}
Now EF can decide which key to generate first (User's) before referring to it by foreign keys. Subsequently you set the FK in User.
But... is this the best model?
Maybe, maybe not.
The issue is that your model does not enforce the business rule that a user can only have one of its own avatars as default avatar. User.AvatarId can refer to any avatar. So you have to write business logic to enforce the business rule.
With YD1m's solution (no User.AvatarId, but a column Avatar.IsDefault) this business rule is enforced implicitly. But now you have to write business logic to enforce that only one avatar is the default.
It's up to you to decide what you think is more feasible.
(for the record: way back, I took the latter option)

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