mirror of
https://github.com/hargata/lubelog.git
synced 2025-12-10 18:36:38 -06:00
scaffolding for authorization.
This commit is contained in:
parent
58873372ec
commit
98f14c1cc0
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
43
Middleware/Authen.cs
Normal 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
13
Models/UserConfig.cs
Normal 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;}
|
||||
}
|
||||
}
|
||||
17
Program.cs
17
Program.cs
@ -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.UseStaticFiles();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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>
|
||||
@ -39,3 +40,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
loadGarage();
|
||||
loadSettings();
|
||||
</script>
|
||||
89
Views/Home/_Settings.cshtml
Normal file
89
Views/Home/_Settings.cshtml
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
1
userConfig.json
Normal file
@ -0,0 +1 @@
|
||||
{"UseDarkMode":true,"EnableCsvImports":true,"UseMPG":true,"UseDescending":false,"EnableAuth":false,"UserNameHash":"","UserPasswordHash":""}
|
||||
@ -44,3 +44,15 @@ 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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user