Integrating ASP.NET Membership’s Authentication and Authorization with Existing Corporate Database with MVC5, Entity Framework 6 – Part 2

construction crew building a website

As discussed in Part 1  of this blog series, ASP.NET Membership allows you to validate and store user credentials with built-in functionalities. It helps you manage user authentication in the web application. ASP.NET Membership is a self-standing feature for authentication. It can also be integrated with ASP.NET role management to provide authorization services for the web applications. Membership can also be integrated with user profile to provide application-specific customization tailored to individual users. 

In this Part 2 of the series, we will address question 3 of the process. 

  1. If you have an existing database, how do you enable it with authentication and authorization? Should you re-invent the wheel, or just use the ASP.NET membership provider?
  2. If we’re using ASP.NET membership, can we integrate it with existing database?
  3. How do you create an MVC application, if one does not already exist? 
  4. How does one handle authentication and authorization?
  5. What is the limitation of the using the MVC5’s Role base data annotation? How do we overcome it?

STEP 3: Building a MVC application using ASP.NET membership, Entity Framework 6 with a Model First approach 

1. Create an ASP.NET Web Application – named SchoolManagementSystem, if web application does not currently exist.
Create a new application window

 

2. Under template, select MVC, click “Change Authentication”
Change Authentication window

 

3. Under “Change Authentication,” choose “No Authentication.” Click “OK” to create the project.
In this example, we chose “No Authentication.” It might be a bit misleading, but the SchoolManagementSystem application will be configured to manually use ASP.NET membership for authentication. If we select Organizational Accounts, the application will be configured to use Windows Identity Foundation (WIF) for authentication in Azure Active Directory, or Windows Server Active Directory. And Windows Authentication is intended for Intranet web applications, which won’t include user registration or login GUI.
Selecting no authentication

 

4. Change the site layout by:

Replace content of "~/Views/Shared/_Layout.cshtml" with following:

 <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - School Management System</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <div class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                @Html.ActionLink("School Management System", "Index", "Home", null, new { @class = "navbar-brand" })
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>@Html.ActionLink("Home", "Index", "Home")</li>
                    <li>@Html.ActionLink("About", "About", "Home")</li>
                    <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
                </ul>
            </div>
        </div>
    </div>
    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; @DateTime.Now.Year - School Management System</p>
        </footer>
    </div>
 

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>
</html>
Replace content of "~/Views/Home/Index.cshtml" with following:


@{
    ViewBag.Title = "Home Page";
}

<div class="jumbotron">
    <h1>School Management System</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>WHERE SCIENCE AND ENGINEERING CONVERGE</h2>
        <p>
            School Management System is a sample application that
            demonstrates how to use Entity Framework 6 in an
            ASP.NET MVC 5 web application with ASP.NET Membership.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Computer Science</h2>
        <p>Undergraduate</p>
        <p>Graduate</p>
        <p><a class="btn btn-default" href="#">Intro &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Applied Mathematics</h2>
        <p>Undergraduate</p>
        <p>Graduate</p>
        <p><a class="btn btn-default" href="#">Tourguide &raquo;</a></p>
    </div>
</div> 

5. You will see the sample application as shown below.
Screenshot of a Sample application

 

6. Add a new “Class Library” project “SMSDB” to host the Entity Framework Designer and then generate a database schema from the model.
Screenshot of adding a new class library

 

7. Project -> Add New Item…

8. Select “Data” from the left menu and then “ADO.NET Entity Data Model,” name it “SMSDB.edmx.”
Screenshot of adding a new item
 

9. Select “Generate from database,” hit “Next.”
Screenshot of step 8
 

10. In the “Choose Your Data Connection” dialog box, click “New Connection.” In the “Choose Data Source” dialog box, select “Microsoft SQL Server” as Data Source and select “.NET Framework Data Provider for SQL Server” for the Data Provider.
Screenshot of choosing data source

11. Enter “SampleServer” (or the actual server name) in “Server name” dropdown, type the credentials for SQL Server Authentication, then select “School” from the “Select or enter a database name” dropdown. Click “Test Connection” to make sure your connection to the “School” database is working. Hit “OK.”
Screenshot of step 11

 

12. In the “Entity Data Model Wizard,” choose “Yes, include the sensitive data in the connection string.” Check checkbox “Save entity connection settings in App.Config as,” and use “SchoolEntities” as the name. Hit “Next.”
Screenshot of step 13

 

13. Choose “Entity Framework 6.0,” and hit “Next.”
Screenshot of step 14

 

14. Check “Tables” from the School database, and pick only the following tables: “aspnet_Applications,” “aspnet_Membership,” “aspnet_Roles,” “aspnet_Users,” and “aspnet_UsersInRoles” from the ASP.NET membership. Hit “Finish.”
Screenshot of step 15

 

15. Hit “OK,” if you see the Security Warning dialog.
Screenshot of step 16

 

16. The Entity Framework model below will be generated.
Entity framework

 

17. Add more properties to the .edmx diagram – Right click in a blank area in the Entity Framework Model Designer -> Add New -> Entity
Adding more properties

 

18. Add “UserPersonPermission” entity as shown below, and hit “OK.”
User permission entity

 

19. Right click on the new entity, Add New -> Association Property. Enter details as shown below to associate aspnet_Users for the UserPersonPermission table. Select “1” to “*(Many)” relationships for Multiplicity.
Step 20

 

20. Right click on the new entity, Add New -> Association Property, enter details as shown below to associate Person table with UserPersonPermission table, which has 1 and (0 or 1) relationship.
Step 21

 

21. Add five more scalar properties: IsLocked, DateCreated, CreatedBy, DateUpdated, and UpdatedBy with details shown as noted below.
IsLocked
DateCreated
CreatedBy
Scalar 4
UpdatedBy

 

22. Below is the model view with the newly added properties.
Screenshot of model

 

23. Next, right click SMSDB.edmx designer and select “Generate Database from Model.”
Generating Database

 

24. Click “Finish.”
DB script

 

25. You’ll see the part of the DB script as shown below.

SET QUOTED_IDENTIFIER OFF;
GO
USE [School];
GO
IF SCHEMA_ID(N'dbo') IS NULL EXECUTE(N'CREATE SCHEMA [dbo]');
GO
 

-- --------------------------------------------------
-- Dropping existing FOREIGN KEY constraints
-- --------------------------------------------------
 

IF OBJECT_ID(N'[dbo].[FK__aspnet_Me__Appli__45F365D3]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[aspnet_Membership] DROP CONSTRAINT [FK__aspnet_Me__Appli__45F365D3];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Me__UserI__46E78A0C]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[aspnet_Membership] DROP CONSTRAINT [FK__aspnet_Me__UserI__46E78A0C];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Ro__Appli__6477ECF3]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[aspnet_Roles] DROP CONSTRAINT [FK__aspnet_Ro__Appli__6477ECF3];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Us__Appli__35BCFE0A]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[aspnet_Users] DROP CONSTRAINT [FK__aspnet_Us__Appli__35BCFE0A];
GO
IF OBJECT_ID(N'[dbo].[FK__aspnet_Us__RoleI__693CA210]', 'F') IS NOT NULL
    ALTER TABLE [dbo].[aspnet_UsersInRoles] DROP CONSTRAINT [FK__aspnet_Us__RoleI__693CA210];
GO
……

Go back to SMSDB.edmx designer and save it.

26. Add the folder: DAL in SchoolManagementSystem project.

This will act as a Data Access Layer (DAL) to the database. Then add an interface: ISMSRepository, to the DAL folder.
New folder

 

Add the following codes to ISMSRepository:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SMSDB;
 

namespace SchoolManagementSystem.DAL
{
    interface ISMSRepository
    {
        void InsertPerson(Person person);
        IEnumerable<Person> GetPerson();
        Person GetPersonById(int personId);
        void UpdatePerson(Person person);
        void DeletePerson(Person person);
        void Save();
    }
}

28. Implement SMSRepository.cs as the following:

namespace SchoolManagementSystem.DAL
{
    public class SMSRepository : ISMSRepository
    {
        private SchoolEntities _dbContext;
        public SMSRepository(SchoolEntities dbContext)
        {
            this._dbContext = dbContext;
        }
 

        public void InsertPerson(Person person)
        {
            _dbContext.People.Add(person);
            _dbContext.SaveChanges();
        }
 

        public IEnumerable<Person> GetPerson()
        {
            return _dbContext.People.ToList();
        }
 

        public Person GetPersonById(int personId)
        {
            return _dbContext.People.Find(personId);
        }
 

        public void UpdatePerson(Person person)
        {
            _dbContext.Entry(person).State = EntityState.Modified;
            _dbContext.SaveChanges();
        }
 

        public void DeletePerson(int personId)
        {
            var personToBeDeleted = _dbContext.People.Find(personId);
            _dbContext.People.Remove(personToBeDeleted);
            _dbContext.SaveChanges();
        }
    }
}

29. Add the class: SMSRepository.cs to DAL as shown below.SMS Repository

 

30. Add PersonController by selecting “MVC5 Controller with views, using Entity Framework.”
Step 31

 

31. Check “Use async controller actions,” select Person (SMSDB) in Model class, SchoolEntities (SMSDB) in Data context class. Click Add.
Step 32

 

32. PersonController.cs should appear as below.

namespace SchoolManagementSystem.Controllers
{
    public class PersonController : Controller
    {
        private SchoolEntities db = new SchoolEntities();
 

        [CustomAuthorize("Admin", "Faculty", "Staff", "Student")]
        public async Task<ActionResult> Index()
        {
            var people = db.People.Include(p => p.OfficeAssignment).Include(p => p.UserPersonPermission);
            return View(await people.ToListAsync());
        }
 

        [CustomAuthorize("Admin", "Faculty", "Staff", "Student")]
        public async Task<ActionResult> Details(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Person person = await db.People.FindAsync(id);
            if (person == null)
            {
                return HttpNotFound();
            }
            return View(person);
        }
 

        [CustomAuthorize("Admin", "Faculty")]
        public ActionResult Create()
        {
            ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location");
            ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy");
            return View();
        }
 

        // POST: /Person/Create
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [CustomAuthorize("Admin", "Faculty")]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Create([Bind(Include="PersonID,LastName,FirstName,HireDate,EnrollmentDate,Discriminator")] Person person)
        {
            if (ModelState.IsValid)
            {
                db.People.Add(person);
                await db.SaveChangesAsync();
                return RedirectToAction("Index");
            }
 

            ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
            ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
            return View(person);
        }
 

        [CustomAuthorize("Admin", "Faculty")]
        public async Task<ActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Person person = await db.People.FindAsync(id);
            if (person == null)
            {
                return HttpNotFound();
            }
            ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
            ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
            return View(person);
        }
 

        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [CustomAuthorize("Admin", "Faculty")]
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Edit([Bind(Include="PersonID,LastName,FirstName,HireDate,EnrollmentDate,Discriminator")] Personperson)
        {
            if (ModelState.IsValid)
            {
                db.Entry(person).State = EntityState.Modified;
                await db.SaveChangesAsync();
                return RedirectToAction("Index");
            }
            ViewBag.PersonID = new SelectList(db.OfficeAssignments, "InstructorID", "Location", person.PersonID);
            ViewBag.PersonID = new SelectList(db.UserPersonPermissions, "Id", "CreatedBy", person.PersonID);
            return View(person);
        }
 

        [CustomAuthorize("Admin", "Faculty")]
        public async Task<ActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Person person = await db.People.FindAsync(id);
            if (person == null)
            {
                return HttpNotFound();
            }
            return View(person);
        }
 

        [CustomAuthorize("Admin", "Faculty")]
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> DeleteConfirmed(int id)
        {
            Person person = await db.People.FindAsync(id);
            db.People.Remove(person);
            await db.SaveChangesAsync();
            return RedirectToAction("Index");
        }
 

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                db.Dispose();
            }
            base.Dispose(disposing);
        }
    }
}
  1. For those who encounter an error: Incompatible SQL Server version, you need to download SSDT for Visual Studio 2013(16.5). The Person’s page appears after pressing F5 in VS 2013.
    Index
     

In Part 3 of the series, we will address the questions related to enabling authentication and authorization and overcoming limitations of the MVC5’s role base data.