dotnetcore-demo / dotnetcore-mvc

Apache License 2.0
0 stars 0 forks source link

Feature - Login #2

Closed anitsh closed 1 year ago

anitsh commented 1 year ago

As a user I should be able to login to the system.

Movies and Users should only be accessible to a user who is logged in.

anitsh commented 1 year ago

An easy way to authorize resource was adding [Authorize] to the class or function.

After adding it to MoviesController without implementing required authentication and authorization, error is thrown:

An unhandled exception occurred while processing the request.
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).

ASP.NET Core MVC provided an easy way to add login, authentication and authorization features. Below are the steps to adding required packages and steps to scaffold necessary files.

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore
dotnet add package Microsoft.AspNetCore.Identity.UI

dotnet aspnet-codegenerator identity --useDefaultUI : Setup identity with the default UI and the minimum number of files

dotnet aspnet-codegenerator identity -dc MvcMovie.Data.MvcMovieContext --files "Account.Register;Account.Login" : Setup identity with the default UI and the minimum number of files

dotnet aspnet-codegenerator identity: Generates all the files

dotnet ef migrations add CreateIdentitySchema : Create schema

dotnet ef database update

ASP.NET Core provides ASP.NET Core Identity as a Razor Class Library. Applications that include Identity can apply the scaffolder to selectively add the source code contained in the Identity Razor Class Library (RCL). You might want to generate source code so you can modify the code and change the behavior. For example, you could instruct the scaffolder to generate the code used in registration. Generated code takes precedence over the same code in the Identity RCL.

The Authorize attribute here is only requiring the user to be logged in to perform a certain action, but the attribute can take properties to have a more fine grained control of authorisation. For example, using the Roles attribute, we can specify which role is allowed to perform a certain action.

[Authorize(Roles = "Manager")]

Need to go through official tutorials on Authentication and Authorization for more deeper understanding.

Resources

anitsh commented 1 year ago

Previous steps to understand .NET Core MVC

Initiate Project

dotnet new mvc -o MvcMovie : Creates a new ASP.NET Core MVC project in the MvcMovie folder.

dotnet run : Run project

MVC Architecture

Models: Classes that represent the data of the app. The model classes use validation logic to enforce business rules for that data. Typically, model objects retrieve and store model state in a database.

Views: Views are the components that display the app's user interface (UI). Generally, this UI displays the model data.

Controllers: Classes that: Handle browser requests, Retrieve model data, Call view templates that return a response.

Movie model retrieves movie data from a database, provides it to the view or updates it. Updated data is written to a database.

Model classes are used with Entity Framework Core (EF Core) to work with a database. EF Core is an object-relational mapping (ORM) framework that simplifies the data access code that we have to write.

The model classes created are known as POCO classes, from Plain Old CLR Objects. POCO classes don't have any dependency on EF Core. They only define the properties of the data to be stored in the database.

Model First

Model classes are created first and EF Core creates the database.

Add tools and dependent packages.

dotnet tool uninstall --global dotnet-aspnet-codegenerator
dotnet tool install --global dotnet-aspnet-codegenerator

dotnet tool uninstall --global dotnet-ef
dotnet tool install --global dotnet-ef

dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

Scaffold Views And Controller

Use the scaffolding tool to produce Create, Read, Update, and Delete (CRUD) pages for the movie model.

export PATH=$HOME/.dotnet/tools:$PATH`

dotnet aspnet-codegenerator controller -name MoviesController -m Movie -dc MvcMovie.Data.MvcMovieContext --relativeFolderPath Controllers --useDefaultLayout --referenceScriptLibraries -sqlite

-m The name of the model. -dc The data context. --relativeFolderPath The relative output folder path to create the files. --useDefaultLayout|-udl The default layout should be used for the views. --referenceScriptLibraries Adds _ValidationScriptsPartial to Edit and Create pages. -sqlite Flag to specify if DbContext should use SQLite instead of SQL Server.

Scaffolding updates the following:

Scaffolding creates the following:

Run Migration

dotnet ef migrations add InitialCreate

dotnet ef database update

ef migrations add InitialCreate: Generates a Migrations/{timestamp}_InitialCreate.cs migration file.

The InitialCreate argument is the migration name. Any name can be used, but by convention, a name is selected that describes the migration. This is the first migration, so the generated class contains code to create the database schema. The database schema is based on the model specified in the MvcMovieContext class, in the Data/MvcMovieContext.cs file.

ef database update: Updates the database to the latest migration, which the previous command created. This command runs the Up method in the Migrations/{time-stamp}_InitialCreate.cs file, which creates the database.

When EF Code First is used to automatically create a database, Code First:

When a change is made in the model, run both commands to track the changes.

Passing Data To View

A controller can pass data or objects to a view using the ViewData dictionary. The ViewData dictionary is a dynamic object that provides a convenient late-bound way to pass information to a view.

MVC provides the ability to pass strongly typed model objects to a view. This strongly typed approach enables compile time code checking.

The @model statement at the top of the view file specifies the type of object that the view expects. When the movie controller was created, the following @model statement was included.

This @model directive allows access to the movie that the controller passed to the view. The Model object is strongly typed.

Validation

The validation support provided by MVC and Entity Framework Core Code First is a good example of the DRY principle in action. We can declaratively specify validation rules in one place (in the model class) and the rules are enforced everywhere in the app.

The DataAnnotations namespace provides a set of built-in validation attributes that are applied declaratively to a class or property. DataAnnotations also contains formatting attributes like DataType that help with formatting and don't provide any validation.

Route Fix With Controleller Method Overload

The common language runtime (CLR) requires overloaded methods to have a unique parameter signature (same method name but different list of parameters).

ASP.NET maps segments of a URL to action methods by name, and if we rename a method, routing normally wouldn't be able to find that method.

A common work around for methods that have identical names and signatures is to artificially change the signature of the POST method to include an extra (unused) parameter.

The MVC scaffolding engine that created the action method adds a comment showing an HTTP request that invokes the method.

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

Use MySQL Database

As the application that needs migration uses MySQL, we want to implement MySQL.

dotnet tool update --global dotnet-ef : upgrade Entity Framework Core.

dotnet add package Pomelo.EntityFrameworkCore.MySql --version=7.0.0-alpha.1: As our .NET Core version is 7.0.0, the latest release was used although that is in alpha.

dotnet ef migrations add UseMySQL : Generate migrations to use with MySQL and it's database context.

dotnet ef database update: Update database changes with the newly generated migrations.

When ever any change is made, new migration needs to be generated and applied to database.

Here we are using Pomelo as the Entity Framework Core provider for MySQL. It is choosen primarily because:

https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql

Create Model scaffold from database: dotnet ef dbcontext scaffold "Server=localhost;User=root;Password=1234;Database=ef" "Pomelo.EntityFrameworkCore.MySql"

Or, generate models from database tables.

Other official option : https://www.nuget.org/packages/MySql.EntityFrameworkCore

Security

Prior, the database connection string was set on appsettings.json file and or was hardcoded on Program.cs.

The connection string is fetched via environment variable.

Store the envionment variable permanently in localfiles like ~/.bashrc or simply export temporarily via export.

export CONNECTION_STRING='Server=localhost;User=root;Password=1234;Database=ef;Port=3306

Port is optional if the MySQL Sever is publicy accessile at port 3306.

Running Application

By default, application runs in Development environmet.

dotnet run --environment Production --urls http://localhost:8080: Set environment and port.

Deployment needs to be researched further. https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-nginx

Generating User Model, View and Controller

https://learn.microsoft.com/en-us/ef/core/cli/dotnet#dotnet-ef-dbcontext-scaffold

dotnet ef dbcontext scaffold "Server=XXX;Database=XXX;User=XXX;Password=XXX;Port=XXX" "Pomelo.EntityFrameworkCore.MySql" -o Models -t User : Generate User model

dotnet aspnet-codegenerator controller -name UsersController -m User -dc MvcMovie.Data.MvcMovieContext --relativeFolderPath Controllers --useDefaultLayout: Generate Users Controller and View

Add To The Project Solution

When to use a solution file to manage several projects as a single unit.

We want to create separate projects based on their concerns and create modular units as projects.

dotnet new sln --name aspdonetcore-example
dotnet new classlib -o BusinessLogic/BusinessLogic -n BusinessLogic
dotnet new nunit --output Test/Unit --name Unit
dotnet new nunit --output Test/Integration --name Integration
dotnet sln add BusinessLogic/BusinessLogic
dotnet sln add Test/Unit 
dotnet sln add Test/Integration
dotnet restore
dotnet build aspdonetcore-example.sln
dotnet run -p MvcMovie 

Add Unit Test

dotnet new nunit --output Test/Unit --name Unit : Adds Nunit testing framework. dotnet add Test/Unit reference BusinessLogic/BusinessLogic.csproj : Add BusinessLogic project reference to be unit tested. dotnet test Test/Unit : Run Unit test. dotnet test Test/Unit --no-build : Run tests on a previous build.

VSCode Soluton Exploer Extension

vscode-solution-explorer https://github.com/fernandoescolar/vscode-solution-explorer

code --list-extensions code --install-extension ms-vscode.cpptools code --uninstall-extension ms-vscode.csharp

dotnet restore - Restores the dependencies and tools of a project.

We don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build, dotnet run, dotnet test, dotnet publish, and dotnet pack.

To disable implicit restore, use the --no-restore option.

The dotnet restore command is still useful in certain scenarios where explicitly restoring makes sense, such as continuous integration builds or in build systems that need to explicitly control when the restore occurs.

The dotnet restore command uses NuGet to restore dependencies as well as project-specific tools that are specified in the project file. In most cases, you don't need to explicitly use the dotnet restore command, since a NuGet restore is run implicitly if necessary.