scaffolding for authorization.

This commit is contained in:
ivancheahhh 2024-01-04 13:18:58 -07:00
parent 58873372ec
commit 98f14c1cc0
17 changed files with 265 additions and 26 deletions

View File

@ -8,9 +8,11 @@ using System.Drawing;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using CarCareTracker.Helper;
using Microsoft.AspNetCore.Authorization;
namespace CarCareTracker.Controllers
{
[Authorize]
public class FilesController : Controller
{
private readonly ILogger<FilesController> _logger;

View File

@ -8,31 +8,74 @@ using System.Drawing;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using CarCareTracker.Helper;
using Microsoft.AspNetCore.Authorization;
namespace CarCareTracker.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IVehicleDataAccess _dataAccess;
private readonly IFileHelper _fileHelper;
private readonly IConfiguration _config;
public HomeController(ILogger<HomeController> logger, IVehicleDataAccess dataAccess, IFileHelper fileHelper)
public HomeController(ILogger<HomeController> logger, IVehicleDataAccess dataAccess, IFileHelper fileHelper, IConfiguration configuration)
{
_logger = logger;
_dataAccess = dataAccess;
_fileHelper = fileHelper;
_config = configuration;
}
public IActionResult Index()
public IActionResult Index(string tab = "garage")
{
return View();
return View(model: tab);
}
public IActionResult Garage()
{
var vehiclesStored = _dataAccess.GetVehicles();
return PartialView("_GarageDisplay", vehiclesStored);
}
public IActionResult Settings()
{
var userConfig = new UserConfig
{
EnableCsvImports = bool.Parse(_config[nameof(UserConfig.EnableCsvImports)]),
UseDarkMode = bool.Parse(_config[nameof(UserConfig.UseDarkMode)]),
UseMPG = bool.Parse(_config[nameof(UserConfig.UseMPG)]),
UseDescending = bool.Parse(_config[nameof(UserConfig.UseDescending)]),
EnableAuth = bool.Parse(_config[nameof(UserConfig.EnableAuth)])
};
return PartialView("_Settings", userConfig);
}
[HttpPost]
public IActionResult WriteToSettings(UserConfig userConfig)
{
try
{
var configFileContents = System.IO.File.ReadAllText("userConfig.json");
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//copy over settings that are off limits on the settings page.
userConfig.EnableAuth = existingUserConfig.EnableAuth;
userConfig.UserNameHash = existingUserConfig.UserNameHash;
userConfig.UserPasswordHash = existingUserConfig.UserPasswordHash;
} else
{
userConfig.EnableAuth = false;
userConfig.UserNameHash = string.Empty;
userConfig.UserPasswordHash = string.Empty;
}
System.IO.File.WriteAllText("userConfig.json", System.Text.Json.JsonSerializer.Serialize(userConfig));
return Json(true);
} catch (Exception ex)
{
_logger.LogError(ex, "Error on saving config file.");
}
return Json(false);
}
public IActionResult Privacy()
{
return View();

View File

@ -5,9 +5,11 @@ using Microsoft.AspNetCore.Mvc;
using CarCareTracker.Helper;
using CsvHelper;
using System.Globalization;
using Microsoft.AspNetCore.Authorization;
namespace CarCareTracker.Controllers
{
[Authorize]
public class VehicleController : Controller
{
private readonly ILogger<HomeController> _logger;

43
Middleware/Authen.cs Normal file
View File

@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text.Encodings.Web;
namespace CarCareTracker.Middleware
{
public class Authen : AuthenticationHandler<AuthenticationSchemeOptions>
{
private IHttpContextAccessor _httpContext;
private bool enableAuth;
public Authen(
IOptionsMonitor<AuthenticationSchemeOptions> options,
UrlEncoder encoder,
ILoggerFactory logger,
IConfiguration configuration,
IHttpContextAccessor httpContext): base(options, logger, encoder)
{
_httpContext = httpContext;
enableAuth = bool.Parse(configuration["EnableAuth"]);
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!enableAuth)
{
//generate a fake user ticket to go with it lol.
var appIdentity = new ClaimsIdentity("Custom");
var userIdentity = new List<Claim>
{
new(ClaimTypes.Name, "admin")
};
appIdentity.AddClaims(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name);
return AuthenticateResult.Success(ticket);
} else
{
return AuthenticateResult.Fail("Invalid credentials");
}
}
}
}

13
Models/UserConfig.cs Normal file
View File

@ -0,0 +1,13 @@
namespace CarCareTracker.Models
{
public class UserConfig
{
public bool UseDarkMode { get; set; }
public bool EnableCsvImports { get; set; }
public bool UseMPG { get; set; }
public bool UseDescending { get; set; }
public bool EnableAuth { get; set; }
public string UserNameHash { get; set; }
public string UserPasswordHash { get; set;}
}
}

View File

@ -1,6 +1,9 @@
using CarCareTracker.External.Implementations;
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
using CarCareTracker.Middleware;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
var builder = WebApplication.CreateBuilder(args);
@ -14,13 +17,21 @@ builder.Services.AddSingleton<ICollisionRecordDataAccess, CollisionRecordDataAcc
builder.Services.AddSingleton<ITaxRecordDataAccess, TaxRecordDataAccess>();
builder.Services.AddSingleton<IFileHelper, FileHelper>();
//Additional JsonFile
builder.Configuration.AddJsonFile("userConfig.json", optional: true, reloadOnChange: true);
//Configure Auth
builder.Services.AddHttpContextAccessor();
builder.Services.AddAuthentication("AuthN").AddScheme<AuthenticationSchemeOptions, Authen>("AuthN", opts => { });
builder.Services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes("AuthN").RequireAuthenticatedUser().Build();
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
}
app.UseExceptionHandler("/Home/Error");
app.UseStaticFiles();
app.UseRouting();

View File

@ -7,7 +7,7 @@
"ASPNETCORE_ENVIRONMENT": "Development"
},
"dotnetRunMessages": true,
"applicationUrl": "http://10.10.2.61:5011"
"applicationUrl": "http://10.10.2.60:5011"
},
"IIS Express": {
"commandName": "IISExpress",

View File

@ -1,4 +1,5 @@
@{
@model string
@{
ViewData["Title"] = "LubeLogger";
}
@section Scripts {
@ -13,20 +14,20 @@
<hr />
<ul class="nav nav-tabs" id="homeTab" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-car-front me-2"></i>Garage</button>
<button class="nav-link @(Model == "garage" ? "active" : "")" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab"><i class="bi bi-car-front me-2"></i>Garage</button>
</li>
<li class="nav-item ms-auto" role="presentation">
<button class="nav-link" id="help-tab" data-bs-toggle="tab" data-bs-target="#help-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-question-circle me-2"></i>Help</button>
<button class="nav-link @(Model == "settings" ? "active" : "")" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-tab-pane" type="button" role="tab"><i class="bi bi-gear me-2"></i>Settings</button>
</li>
</ul>
<div class="tab-content" id="homeTab">
<div class="tab-pane fade show active" id="garage-tab-pane" role="tabpanel" tabindex="0">
<div class="tab-pane fade @(Model == "garage" ? "show active" : "")" id="garage-tab-pane" role="tabpanel" tabindex="0">
<div class="row">
<div id="garageContainer" class="row gy-3 align-items-stretch">
</div>
</div>
</div>
<div class="tab-pane fade" id="help-tab-pane" role="tabpanel" tabindex="0">
<div class="tab-pane fade @(Model == "settings" ? "show active" : "")" id="settings-tab-pane" role="tabpanel" tabindex="0">
</div>
</div>
@ -38,4 +39,8 @@
</div>
</div>
</div>
</div>
<script>
loadGarage();
loadSettings();
</script>

View File

@ -0,0 +1,89 @@
@model UserConfig
<div class="row">
<div class="d-flex justify-content-center">
<h6 class="display-6 mt-2">Settings</h6>
</div>
<hr />
<div class="col-12 col-md-6">
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableDarkMode" checked="@Model.UseDarkMode">
<label class="form-check-label" for="enableDarkMode">Dark Mode</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableCsvImports" checked="@Model.EnableCsvImports">
<label class="form-check-label" for="enableCsvImports">Enable CSV Imports</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useMPG" checked="@Model.UseMPG">
<label class="form-check-label" for="useMPG">Use Imperial Units for Fuel Economy Calculations(Miles, Gallons)</label>
</div>
</div>
<div class="col-12 col-md-6">
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="useDescending" checked="@Model.UseDescending">
<label class="form-check-label" for="useDescending">Sort lists in Descending Order(Newest to Oldest)</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" onChange="updateSettings()" type="checkbox" role="switch" id="enableAuth" checked="@Model.EnableAuth">
<label class="form-check-label" for="enableAuth">Enable Authentication</label>
</div>
</div>
</div>
<div class="row">
<div class="d-flex justify-content-center">
<h6 class="display-6 mt-2">About</h6>
</div>
<hr />
<div class="col-12 col-md-6">
<div class="d-flex justify-content-center">
<img src="/defaults/lubelogger_logo.png" />
</div>
<p class="lead">
Proudly developed in the rural town of Price, Utah by Hargata Softworks.
</p>
<p class="lead">If you enjoyed using this app, please consider spreading the good word.</p>
<div class="d-flex justify-content-center">
<h6 class="display-7 mt-2">Hometown Shoutout</h6>
</div>
<p class="lead">
Do you work remotely and are looking for a new place to call home? Consider looking into the rural Eastern Utah town of Price. Price and Carbon County
has experienced pronounced decline in both population and economic activity within the past decade whereas the rest of the state experienced exponential growth.
It is conveniently located in between Salt Lake City and Moab Utah. Amenities are relatively complete in terms of big box stores and high speed fiber Internet.
Price and its surrounding towns as a whole could really benefit from in-migration. Thank you!
</p>
</div>
<div class="col-12 col-md-6">
<div class="d-flex justify-content-center">
<h6 class="display-7 mt-2">Open Source Dependencies</h6>
</div>
<p class="lead">
LubeLogger utilizes open-source dependencies to serve you the best possible user experience, those dependencies are:
</p>
<ul class="list-group">
<li class="list-group-item">Bootstrap</li>
<li class="list-group-item">Bootstrap-DatePicker</li>
<li class="list-group-item">LiteDB</li>
<li class="list-group-item">SweetAlert2</li>
<li class="list-group-item">CsvHelper</li>
<li class="list-group-item">Chart.js</li>
</ul>
</div>
</div>
<script>
function updateSettings(){
var userConfigObject = {
useDarkMode: $("#enableDarkMode").is(':checked'),
enableCsvImports: $("#enableCsvImports").is(':checked'),
useMPG: $("#useMPG").is(':checked'),
useDescending: $("#useDescending").is(':checked')
}
$.post('/Home/WriteToSettings', { userConfig: userConfigObject}, function(data){
if (data) {
setTimeout(function () { window.location.href = '/Home/Index?tab=settings' }, 500);
} else {
errorToast("An error occurred, please try again later.")
}
})
}
</script>

View File

@ -1,7 +1,8 @@
<!DOCTYPE html>
@inject IConfiguration Configuration
@{
var useDarkMode = bool.Parse(Configuration["DarkMode"]);
var useDarkMode = bool.Parse(Configuration["UseDarkMode"]);
var enableCsvImports = bool.Parse(Configuration["EnableCsvImports"]);
}
<html lang="en" data-bs-theme="@(useDarkMode ? "dark" : "light")">
<head>
@ -20,6 +21,14 @@
<script src="~/lib/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
<script src="~/sweetalert/sweetalert2.all.min.js"></script>
<script src="~/js/loader.js"></script>
<script>
function getGlobalConfig() {
return {
useDarkMode : "@useDarkMode" == "True",
enableCsvImport : "@enableCsvImports" == "True"
}
}
</script>
@await RenderSectionAsync("Scripts", required: false)
</head>
<body>

View File

@ -14,7 +14,7 @@
<div class="row">
<div class="d-flex justify-content-between">
<button onclick="returnToGarage()" class="btn btn-secondary btn-md mt-1 mb-1"><i class="bi bi-arrow-left-square"></i></button>
<h1 class="text-truncate">@($"{Model.Year} {Model.Make} {Model.Model}(License # {Model.LicensePlate})")</h1>
<h1 class="text-truncate display-4">@($"{Model.Year} {Model.Make} {Model.Model}")<small class="text-body-secondary">@($"(#{Model.LicensePlate})")</small></h1>
<button onclick="editVehicle(@Model.Id)" class="btn btn-warning btn-md mt-1 mb-1"><i class="bi bi-pencil-square"></i></button>
</div>
</div>

View File

@ -3,6 +3,7 @@
<script>
renderChart();
function renderChart() {
var useDarkMode = getGlobalConfig().useDarkMode;
new Chart($("#pie-chart"), {
type: 'pie',
data: {
@ -25,13 +26,13 @@
legend: {
position: "bottom",
labels: {
color: "#fff"
color: useDarkMode ? "#fff" : "#000"
}
},
title: {
display: true,
text: "Expenses by Type",
color: "#fff"
color: useDarkMode ? "#fff" : "#000"
},
}
}

View File

@ -5,6 +5,7 @@
function renderChart() {
var barGraphLabels = [];
var barGraphData = [];
var useDarkMode = getGlobalConfig().useDarkMode;
@foreach (GasCostForVehicleByMonth gasCost in Model)
{
@:barGraphLabels.push("@gasCost.MonthName");
@ -26,7 +27,7 @@
plugins: {
legend: {
labels: {
color: '#fff'
color: useDarkMode ? "#fff" : "#000"
}
}
},
@ -34,12 +35,12 @@
y: {
beginAtZero: true,
ticks: {
color: "#fff"
color: useDarkMode ? "#fff" : "#000"
}
},
x: {
ticks: {
color: "#fff"
color: useDarkMode ? "#fff" : "#000"
}
}
}

View File

@ -6,6 +6,11 @@
}
},
"AllowedHosts": "*",
"DarkMode": true,
"EnableCsvImports": true
"UseDarkMode": false,
"EnableCsvImports": true,
"UseMPG": true,
"UseDescending": false,
"EnableAuth": false,
"UserNameHash": "",
"UserPasswordHash": ""
}

1
userConfig.json Normal file
View File

@ -0,0 +1 @@
{"UseDarkMode":true,"EnableCsvImports":true,"UseMPG":true,"UseDescending":false,"EnableAuth":false,"UserNameHash":"","UserPasswordHash":""}

View File

@ -43,4 +43,16 @@ html {
.vehicleNoteContainer{
height:40vh;
}
.display-7 {
font-size: calc(1.325rem + 0.9vw);
font-weight: 300;
line-height: 1.2;
}
@media (min-width: 1200px) {
.display-7 {
font-size: 2rem;
}
}

View File

@ -10,12 +10,14 @@
function hideAddVehicleModal() {
$('#addVehicleModal').modal('hide');
}
$(document).ready(function () {
loadGarage();
});
//refreshable function to reload Garage PartialView
function loadGarage() {
$.get('/Home/Garage', function (data) {
$("#garageContainer").html(data);
});
}
function loadSettings() {
$.get('/Home/Settings', function (data) {
$("#settings-tab-pane").html(data);
});
}