Add authentication
This commit is contained in:
parent
8f0c4c0a45
commit
a2d84e182d
11 changed files with 206 additions and 21 deletions
54
Website/Controllers/AccountController.cs
Normal file
54
Website/Controllers/AccountController.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Website.Data;
|
||||
using Website.Models;
|
||||
using Website.ViewModels;
|
||||
|
||||
namespace Website.Controllers {
|
||||
public class AccountController:Controller {
|
||||
private readonly UserRepository _repo;
|
||||
|
||||
public AccountController(UserRepository repo) => _repo = repo;
|
||||
|
||||
public IActionResult Index() => View();
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Login(string returnUrl, bool failedAttempt = false) {
|
||||
var model = new LoginViewModel {
|
||||
ReturnUrl = returnUrl,
|
||||
FailedAttempt = failedAttempt
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Login(LoginRequest request) {
|
||||
try {
|
||||
var user = await _repo.GetUserByEmail(request.Username);
|
||||
return user.ValidatePassword(request.Password)
|
||||
? await SetIdentityAndRedirect(request.ReturnUrl, user)
|
||||
: Login(request.ReturnUrl, true);
|
||||
}
|
||||
catch (Exception e) {
|
||||
return Login(request.ReturnUrl, true);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<IActionResult> SetIdentityAndRedirect(string returnUrl, User user) {
|
||||
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, user.Username));
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, user.Username));
|
||||
|
||||
var principal = new ClaimsPrincipal(identity);
|
||||
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
|
||||
|
||||
return string.IsNullOrEmpty(returnUrl)
|
||||
? (IActionResult) RedirectToAction(nameof(Index))
|
||||
: Redirect(returnUrl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Website.Data;
|
||||
using Website.Models;
|
||||
|
@ -38,6 +39,7 @@ namespace Website.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Edit(int? id) {
|
||||
if (!id.HasValue)
|
||||
return View();
|
||||
|
@ -56,6 +58,7 @@ namespace Website.Controllers
|
|||
}
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Save(BlogPostSubmission submission) {
|
||||
var post = submission.Id.HasValue ? await _repo.GetPostByIdAsync(submission.Id.Value) : new BlogPost();
|
||||
|
@ -68,12 +71,14 @@ namespace Website.Controllers
|
|||
return RedirectToAction(nameof(Edit), new{savedPost.Id});
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Manage() {
|
||||
var posts = await _repo.GetAllPostsAsync();
|
||||
var models = posts.OrderByDescending(post => post.Timestamp).Select(post => new BlogPostViewModel(post));
|
||||
return View(models);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Publish(int id) {
|
||||
var post = await _repo.GetPostByIdAsync(id);
|
||||
post.Publish();
|
||||
|
@ -82,6 +87,7 @@ namespace Website.Controllers
|
|||
return RedirectToAction(nameof(Manage));
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Delete(int id) {
|
||||
await _repo.DeletePostAsync(id);
|
||||
|
||||
|
|
10
Website/Data/States/UserState.cs
Normal file
10
Website/Data/States/UserState.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Website.Data.States {
|
||||
public class UserState {
|
||||
public string User_Id { get; set; }
|
||||
public string User_Email { get; set; }
|
||||
public string User_Password { get; set; }
|
||||
public string User_Created { get; set; }
|
||||
public string User_Deleted { get; set; }
|
||||
public string Group_Id { get; set; }
|
||||
}
|
||||
}
|
25
Website/Data/UserRepository.cs
Normal file
25
Website/Data/UserRepository.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Dapper;
|
||||
using Website.Data.States;
|
||||
using Website.Models;
|
||||
|
||||
namespace Website.Data {
|
||||
public class UserRepository {
|
||||
private readonly IDatabaseProvider _dbProvider;
|
||||
|
||||
public UserRepository(IDatabaseProvider dbProvider) {
|
||||
_dbProvider = dbProvider;
|
||||
}
|
||||
|
||||
public async Task<User> GetUserByEmail(string email) {
|
||||
const string query = "SELECT * FROM users WHERE user_email=@email";
|
||||
|
||||
using (var connection = _dbProvider.NewConnection()) {
|
||||
connection.Open();
|
||||
var result = await connection.QueryAsync<UserState>(query, new {email});
|
||||
return new User(result.Single());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
Website/Models/LoginRequest.cs
Normal file
7
Website/Models/LoginRequest.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace Website.Models {
|
||||
public class LoginRequest {
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
}
|
29
Website/Models/User.cs
Normal file
29
Website/Models/User.cs
Normal file
|
@ -0,0 +1,29 @@
|
|||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Website.Data.States;
|
||||
|
||||
namespace Website.Models {
|
||||
public class User {
|
||||
public User(UserState state) {
|
||||
Username = state.User_Email;
|
||||
Password = state.User_Password;
|
||||
}
|
||||
|
||||
public bool ValidatePassword(string password) {
|
||||
using (var sha256 = SHA256.Create()) {
|
||||
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(password));
|
||||
|
||||
var builder = new StringBuilder();
|
||||
foreach (var b in hash)
|
||||
builder.Append(b.ToString("x2"));
|
||||
var hashString = builder.ToString();
|
||||
|
||||
return hashString == Password;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -27,15 +28,18 @@ namespace Website
|
|||
options.MinimumSameSitePolicy = SameSiteMode.None;
|
||||
});
|
||||
|
||||
services.AddSingleton<IConfiguration>(Configuration);
|
||||
services.AddSingleton(Configuration);
|
||||
RegisterRepositories(services);
|
||||
|
||||
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
|
||||
|
||||
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
|
||||
}
|
||||
|
||||
private void RegisterRepositories(IServiceCollection services) =>
|
||||
services.AddSingleton<IDatabaseProvider, MySQLDatabaseProvider>()
|
||||
.AddSingleton<BlogRepository, BlogRepository>();
|
||||
.AddSingleton<BlogRepository, BlogRepository>()
|
||||
.AddSingleton<UserRepository, UserRepository>();
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
|
||||
|
@ -51,24 +55,23 @@ namespace Website
|
|||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseStatusCodePagesWithReExecute("/Error/PageNotFound");
|
||||
app.UseHttpsRedirection();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.UseMvc(routes =>
|
||||
{
|
||||
routes.MapRoute(
|
||||
name: "blogPages",
|
||||
template: "blog/{action}/{page:int}",
|
||||
defaults: new { controller = "Blog", action = "Page", page = 1 });
|
||||
routes.MapRoute(
|
||||
name: "blogView",
|
||||
template: "blog/view/{url}",
|
||||
defaults: new { controller = "Blog", action = "Entry"});
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
app.UseStatusCodePagesWithReExecute("/Error/PageNotFound")
|
||||
.UseHttpsRedirection()
|
||||
.UseStaticFiles()
|
||||
.UseAuthentication()
|
||||
.UseMvc(routes => {
|
||||
routes.MapRoute(
|
||||
name: "blogPages",
|
||||
template: "blog/{action}/{page:int}",
|
||||
defaults: new {controller = "Blog", action = "Page", page = 1});
|
||||
routes.MapRoute(
|
||||
name: "blogView",
|
||||
template: "blog/view/{url}",
|
||||
defaults: new {controller = "Blog", action = "Entry"});
|
||||
routes.MapRoute(
|
||||
name: "default",
|
||||
template: "{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
6
Website/ViewModels/LoginViewModel.cs
Normal file
6
Website/ViewModels/LoginViewModel.cs
Normal file
|
@ -0,0 +1,6 @@
|
|||
namespace Website.ViewModels {
|
||||
public class LoginViewModel {
|
||||
public string ReturnUrl { get; set; }
|
||||
public bool FailedAttempt { get; set; }
|
||||
}
|
||||
}
|
8
Website/Views/Account/Index.cshtml
Normal file
8
Website/Views/Account/Index.cshtml
Normal file
|
@ -0,0 +1,8 @@
|
|||
@model object
|
||||
|
||||
@{
|
||||
ViewBag.Title = "title";
|
||||
Layout = "_Layout";
|
||||
}
|
||||
|
||||
<h2>title</h2>
|
20
Website/Views/Account/Login.cshtml
Normal file
20
Website/Views/Account/Login.cshtml
Normal file
|
@ -0,0 +1,20 @@
|
|||
@model LoginViewModel
|
||||
|
||||
@{
|
||||
ViewBag.Title = "Login";
|
||||
}
|
||||
|
||||
@if (!string.IsNullOrEmpty(Model.ReturnUrl)) {
|
||||
<div>Please log in to perform that action</div>
|
||||
}
|
||||
|
||||
@if (Model.FailedAttempt) {
|
||||
<div>Could not log in with those credentials</div>
|
||||
}
|
||||
|
||||
<form method="post" action="login">
|
||||
<input type="hidden" name="returnUrl" value="@Model.ReturnUrl"/>
|
||||
<label for="username">Username: </label><input name="username" id="username"/>
|
||||
<label for="password">Password: </label><input name="password" type="password" id="password"/>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
Loading…
Add table
Add a link
Reference in a new issue