MVC5 custom authorize attribute against string in SQL table - c#

I have read hundreds of articles on ASP.NET MVC Authorization attributes and i think i make it difficult than as it should be. I setup a class in ASP.NET identity as below:
public class UserDetails : IdentityUser
{
public virtual MembershipSerial MembershipSerial { get; set; }
}
public class MembershipSerial
{
[HiddenInput(DisplayValue=false)]
public int Id { get; set; }
[HiddenInput(DisplayValue=false)]
public string Serial { get; set; }
[Required]
[Display(Name="Membership Serial")]
public string SerialConfirmed { get; set; }
}
public class MyDbContext : IdentityDbContext<UserDetails>
{
public MyDbContext()
: base ("EFDbContext")
{
}
public System.Data.Entity.DbSet<MembershipSerial> MembershipSerial { get; set; }
}
I would like to achieve something like below with Authorize Attribute:
[AuthorizeUser(AccessLevels="Has a valid serial key and can place an order")]
public ActionResult PlaceOrder(int ProductID)
{
// some code...
return View();
}
[AuthorizeUser(AccessLevels="Has a valid login and can add items to cart")]
public ActionResult AddToCart(int ProductID)
{
// some code...
return View();
}
[AuthorizeUser(AccessLevels="Has no login and valid serialkey, anonymous ")]
public ActionResult Anonymous(int ProductID)
{
// some code...
return View();
}
Note: The property Serial is added by the system administrator and the property SerialConfirmed will be added by the user. The property SerialConfirmed should be compared to the Serial in the backend if the results where okay then should return true else false.
Is that possible? How?

Related

Asp.net adding Values to Database

Im new to asp.net and i started with basic crud operations.
I made the css class with db name values (code,name,price,currency) and DBcontext for this, when i made the controller it doesnt not add the values to the database but it's stored in somewhere.
When i use as a normal query from c# code, it's stored into the database.
My question is why does the CRUD operations from the (csfile and context) are not in the database?
--cs class--
public class Productos
{
[Key]
public int Codigo { get; set; }
public string Nombre { get; set; }
public decimal Precio { get; set; }
public string Moneda { get; set; }
}
-product context
public class ProductoContext : DbContext
{
public ProductoContext() : base("database name")
{
}
public DbSet<Productos> Producto { get; set; }
}
---this is the code to create---
// GET: Productos/Create
public ActionResult Create()
{
return View();
}
// POST: Productos/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Codigo,Nombre,Precio,Moneda")] Productos productos)
{
if (ModelState.IsValid)
{
db.Producto.Add(productos);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(productos);
}
---- connection string------

Identity Roles with Database First ASP.Net MVC 5 and Entity Framework

I am trying to follow tutorials online to help me get roles setup in my MVC project, but seem to be failing to understand everything. My company required that I create my project Database First, rather than code first, so my roles already exist in my dbContext. I do not want to create a controller to Index, Create, Edit, etc. my Roles, at least not at this step, but I DO want to know what userGroup my users belong to, to then refer to their userGroup as their role.
So, I have two Models; one for users and one for userGroups (essentially a "roles" table):
public partial class User : IdentityUser
{
public int UKey { get; set; }
public int UgKey { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string EmailAddress { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool UseBrightreeAuth { get; set; }
public bool Active { get; set; }
}
public partial class UserGroup
{
public int UgKey { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
I have a Account Controller that handles the "logging in" of my website:
public ActionResult Login(LoginViewModel login)
{
if (!ModelState.IsValid)
{
ViewBag.Error = "Form is not valid; please review and try again.";
return View("Login");
}
using (var wfc = new WorkflowContext())
{
if(wfc.User.Where(x => x.Username == login.Username).Any())
{
//ignore the bad logic/LINQ, it's temporary...
var UserManager = new UserManager<User>(new UserStore<User>(wfc));
UserManager.AddToRole("UserName", "UserRole");
}
else
{
ViewBag.Error = "Credentials invalid. Please try again.";
return View("Login");
}
}
}
the following line of code will not compile, saying wfc is not a valid argument, but I am not sure what I should be using instead?
//Where wfc is the Context that contains my User and UserGroup Tables
var UserManager = new UserManager<User>(new UserStore<User>(wfc));
I get the error
Cannot convert from "Workflow.Models.WorkflowContext" to "System.Data.Entity.DbContext"
Is there a better way to add a user to a role or am I misunderstanding the process? Thanks.

Model and Membership Provider MVC4

I need your help in guiding may into good way of implement the Model in MVC4.
I will let you see my model. But I really don’t know how to link that to Membership Provider in MVC4
I want to build tender application system and I have the following models
Tender: who add projects?
Supplier/provider : who bid for projects
Projects: Projects added by tenders ( Done)
Requirements: each projects had several requirements.(Done)
I did the project And requirement Model.. But am not sure how to do the tender and suppliers!! Because both of them have to register ..!?
2.Is my relation many to many is correct? Between Project and Requirement table.?
Now those are my model with context:
public class ProjectContext : DbContext
{
public ProjectContext()
: base("ProjectsDB")
{
}
public DbSet<ProjectEntry> Entries { get; set; }
public DbSet<Requiernments> RequiernmentEntries { get; set; }
//public DbSet<UserProfile> UserProfiles { get; set; }
}
public class ProjectEntry
{
[Key]
public int ID { get; set; }
[Required]
public string ProjectName { get; set; }
public string Description { get; set; }
public string Statue {get; set; }
public string UplodedFiles { get; set; }
public string Budget { get; set; }
public string EstimateTime { get; set; }
public string Criterias { get; set; }
public DateTime? DueDate { get; set; }
}
public class Requiernments
{
[Key]
public int RequiernmentId { get; set; }
public int ID { get; set; }
public string RequiernmentName { get; set; }
/// <summary>
/// 1: Must to Have
/// 2: Nice to Have
/// 3: Should have
/// </summary>
public string RequiernmentType { get; set; }
public string RequiernmentPrioritet { get; set; }
public float RequiernmenWhight { get; set; }
public string ProviderAnswer { get; set; }
public string ProviderComments{ get; set; }
}:
update 2:
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
if (!Roles.RoleExists("Admin"))
Roles.CreateRole("Admin");
if (!Roles.RoleExists("Member"))
Roles.CreateRole("Member");
if (!Roles.RoleExists("Tender"))
Roles.CreateRole("Tender");
if (!Roles.RoleExists("Provider"))
Roles.CreateRole("Provider");
WebSecurity.CreateUserAndAccount(model.UserName, model.Password,
new
{
EmailAddress = model.EmailAddress
}, false);
Roles.AddUserToRole(model.UserName, "Member");
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
AND
<connectionStrings>
<add name="DefaultConnection" connectionString="Data Source=user-Pc\SQL2012;Initial Catalog=MemberDB;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MemberDB.mdf" providerName="System.Data.SqlClient" />
</connectionStrings>
image:
http://i58.tinypic.com/2rp8i86.png
If I understand correctly, in your application you have two roles, tender and supplier. In addition, you want to tender to be able to add project and then associate requirements with project.
In order to achieve that, first of all you need to config SimpleMembershipProvider to have two roles "tender" and "supplier"
First in the configuration file, replace the classic membership provider with SimpleMembershipProvider by doing this
enable migrations
seed membership and roles
protected override void Seed(MovieDb context)
{
//context.Movies.AddOrUpdate(...);
// ...
SeedMembership();
}
private void SeedMembership()
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection",
"UserProfile", "UserId", "UserName", autoCreateTables: true);
var roles = (SimpleRoleProvider) Roles.Provider;
var membership = (SimpleMembershipProvider) Membership.Provider;
if (!roles.RoleExists("Admin"))
{
roles.CreateRole("Admin");
}
if (membership.GetUser("sallen",false) == null)
{
membership.CreateUserAndAccount("sallen", "imalittleteapot");
}
if (!roles.GetRolesForUser("sallen").Contains("Admin"))
{
roles.AddUsersToRoles(new[] {"sallen"}, new[] {"admin"});
}
}
step 1,2,3 reference:
Scott Allen's blog
Now create your model
public class Tender
{
public int TenderId { get;set;}
public int UserId {get;set;} //this links to the userid in the UserProfiles table
public virtual ICollection<Project> Projects {get;set;}
}
public class Project
{
public int ProjectId {get;set;}
public int TenderId {get;set;}
public virtual Tender Tender {get;set;}
public virtual ICollection<Supplier> Suppliers {get;set;}
public virtual ICollection<Requirement> Requirements {get;set;}
}
public class Supplier
{
public int SupplierId {get;set;}
public virtual ICollection<Project> Projects {get;set;}
}
public class Requirement
{
public int RequirmentId {get;set;}
public int ProjectId {get;set;}
public virtual Project Project {get;set;}
}
because supplier can bid multiple projects and projects can have multiple bidders, therefore supplier and project have a multiple to multiple relationship, you probably want to have a mapping table.
In the OnModelCreating Method,
modelBuilder.Entity<Project>()
.HasMany(p => p.Suppliers)
.WithMany(s => s.Projects)
.Map(map =>
{
map.ToTable("Project_Supplier_Map")
.MapLeftKey("SupplierId")
.MapRightKey("ProjectId");
});
Now you have your model and just need to decorate your class with Authorize attribute

Scaffolding ModelView creates underlying database tables

I'm trying to use ViewModels for the first time using AutoMapper. I have two models:
public class Item
{
public int ItemId { get; set; }
public bool Active { get; set; }
public string ItemCode { get; set; }
public string Name { get; set; }
public List<ItemOption> ItemOptions { get; set; }
//...
}
public class ItemOption
{
public int ItemOptionId { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
//...
}
Which I have turned into two ViewModels:
public class ItemDetailViewModel
{
public int ItemDetailViewModelId { get; set; }
public int ItemId { get; set; }
public bool Active { get; set; }
public string ItemCode { get; set; }
public string Name { get; set; }
public List<ItemDetailItemOptionViewModel> ItemOptions { get; set; }
}
public class ItemDetailItemOptionViewModel
{
public int ItemDetailItemOptionViewModelId { get; set; }
public int ItemOptionId { get; set; }
public string Name { get; set; }
public string Barcode { get; set; }
}
I then set the following in my application start-up:
Mapper.CreateMap<Item, ItemDetailViewModel>();
Mapper.CreateMap<ItemOption, ItemDetailItemOptionViewModel>();
Finally I scaffolded my ItemDetailViewModel:
I then built my project and added a new Item through /Item/Create
I had a look in the database expecting to see that I would have an entry in the Item table, but instead I have ItemDetailViewModel and ItemDetailItemOptionViewModel tables, which I wasn't expecting and the data is is ItemDetailViewModel.
I assume I have done something wrong with my scaffolding? How do I scaffold off the ViewModel without making it part of the main business models?
Further Details
If it isn't possible to scaffold the controller with a ViewModel, then how do I reference the ViewModel in the controller and save changes back to the database?
For example what would the following change to once I remove ItemDetailViewModel from the db context?
//
// POST: /Item/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel itemdetailviewmodel)
{
if (ModelState.IsValid)
{
db.ItemDetailViewModels.Add(itemdetailviewmodel);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(itemdetailviewmodel);
}
Further Details [2]
So am I correct that my Index/Details should work as so or is there a better way of doing it?
//
// GET: /Item/
public ActionResult Index()
{
var items = db.Items.ToList();
var itemdetailviewmodel = AutoMapper.Mapper.Map<ItemDetailViewModel>(items);
return View(itemdetailviewmodel);
}
//
// GET: /Item/Details/5
public ActionResult Details(int id = 0)
{
ItemDetailViewModel itemdetailviewmodel = AutoMapper.Mapper.Map<ItemDetailViewModel>(db.Items.Find(id));
if (itemdetailviewmodel == null)
{
return HttpNotFound();
}
return View(itemdetailviewmodel);
}
Scaffolding is not that intelligent. The standard controller scaffolding template is creating a DbContext with the controller model and presumes you are working with the DB models, not view models and it does not use Automapper. So you'll need to either not use scaffolding, or check what it has done before using it.
And nothing is wrong with the way you use scaffolding, it is just not supposed to do what you expect.
Update this is how you do this without scaffolding
// Without Automapper
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel model)
{
if (ModelState.IsValid)
{
var item = new Item()
{
Active = model.Active,
ItemCode = model.ItemCode,
Name = model.Name,
ItemOptions = // code to convert from List<ItemDetailItemOptionViewModel> to List<ItemOption>
}
db.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
// with Automapper - not recommended by author of Automapper
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ItemDetailViewModel model)
{
if (ModelState.IsValid)
{
var item = Automapper.Mapper.Map<Item>(model);
db.Items.Add(item);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
You'll need to modify your DbContext to have IDbSet<Item> Items instead of IDbSet<ItemDetailViewModels> ItemDetailViewModels.
Automapper was created to map from Domain Models to View Models and not the other way. I have done that for a while, but this is troublesome and causes subtle bugs and other maintenance problems. Even Jimmy Bogard himself says you should not map from view models to domain models.

Cannot open generated database from EF Code first

The error I am getting is "Cannot open database "NerdlyDB" requested by the login. The login failed.
Login failed for user 'ComputerName\User Name'.
This is my connection string:
<add name="NerdlyDB" connectionString="Data Source=(LocalDb)\v11.0; Integrated Security=SSPI; initial catalog= NerdlyDB" providerName="System.Data.SqlClient"/>
This database was generates by using EF code first approach. I am new to this one, so I will show you what I did in case it is off somewhere:
On of my Entity Classes:
namespace NerdlyThings.Models
{
public class Post
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Content { get; set; }
public DateTime Date { get; set; }
public string Tags { get; set; }
}
}
DBContext Class
namespace NerdlyThings.Models
{
public class NerdlyDB : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Image> Images { get; set; }
public DbSet<Quote> Quotes { get; set; }
}
}
I see that the error is obvious an authentication issue, but I don't know where to set it using code first, only via setting up a db in sql server management studio.
*EDIT***
Ok, so I am not by the computer I originally did this on, but I had some time to kill at work so gave this another go by following the simple instructions here
I did this in Visual Studio 2012 RC in an MVC4 internet application template. Works like a dream and I can only assume I have either some strange configuration issue on my other computer, or something got messed up along the way. Anyway here is what I did:
Classes:
namespace CodeFirstBlog.Models
{
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
}
}
DBContext Class:
namespace CodeFirstBlog.Models
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
}
}
I set these up and then created a controller like so (Just had it generated my selecting my context and the class):
public class BlogController : Controller
{
private BlogContext db = new BlogContext();
//
// GET: /Blog/
public ActionResult Index()
{
return View(db.Blogs.ToList());
}
//
// GET: /Blog/Details/5
public ActionResult Details(int id = 0)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
return HttpNotFound();
}
return View(blog);
}
//
// GET: /Blog/Create
public ActionResult Create()
{
return View();
}
//
// POST: /Blog/Create
[HttpPost]
public ActionResult Create(Blog blog)
{
if (ModelState.IsValid)
{
db.Blogs.Add(blog);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(blog);
}
//
// GET: /Blog/Edit/5
public ActionResult Edit(int id = 0)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
return HttpNotFound();
}
return View(blog);
}
//
// POST: /Blog/Edit/5
[HttpPost]
public ActionResult Edit(Blog blog)
{
if (ModelState.IsValid)
{
db.Entry(blog).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(blog);
}
//
// GET: /Blog/Delete/5
public ActionResult Delete(int id = 0)
{
Blog blog = db.Blogs.Find(id);
if (blog == null)
{
return HttpNotFound();
}
return View(blog);
}
//
// POST: /Blog/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Blog blog = db.Blogs.Find(id);
db.Blogs.Remove(blog);
db.SaveChanges();
return RedirectToAction("Index");
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
Then I simply ran the application, used the generated views to create a new Blog. Works great. The database was generated in my App_Data folder, I can access it fine and see the generated schema. So problem solved, maybe? From this point I can use your suggested answers to tweak db settings and whatnot, so thank you.
Are you connecting your DBContext class to that connection string somewhere? My guess is that is missing and therefore causing the problem. I've usually seen the connection string specified in the constructor of the DBContext class (see example below):
public class NerdlyDB : DbContext
{
public NerdlyDB() : base("NerdlyDB")
{
}
...
This will then look for a <connectionString> with name="NerdlyDB" in your web.config / app.config. See this link for more info on setting up EF with your connection string.
I would also recommend using #Eric J.'s approach in your code. You can see a pretty in depth example of his suggestion (and EF code first in general) at silk.codeplex.com (specifically the MileageStats.Data.SqlCe project)
On a separate note, it looks like you are missing the actual code first model setup. Check out the MileageStatsDbContext.cs file in the Silk project to see an example of how to do that.
The best approach I have found so far is to create my own IDatabaseInitializer to run commands that I need to create indices and create users.
public class MyContext : DbContext
{
static private Initializer DbInitializer;
static MyContext()
{
DbInitializer = new MyContext.Initializer();
Database.SetInitializer<MyContext>(DbInitializer);
}
}
public class Initializer : IDatabaseInitializer<MyContext>
{
public void InitializeDatabase(MyContext context)
{
string ddl = "(Some SQL command to e.g. create an index or create a user)";
context.Database.ExecuteSqlCommand(ddl);
}
}

Categories