added basic functionalities (login, create/del/rename pages)

This commit is contained in:
taywon18 2024-09-22 11:48:59 +02:00
parent d531454166
commit 5707f5a2d4
12 changed files with 374 additions and 31 deletions

12
Tagger/Configuration.cs Normal file
View File

@ -0,0 +1,12 @@
namespace Tagger;
public class User
{
public string Name { get; set; } = "";
public string Password { get; set; } = "";
}
public class Configuration
{
public List<User> Users { get; set; } = new();
}

View File

@ -1,12 +1,8 @@
@page "/"
<PageTitle>Index</PageTitle>
<PageTitle>Tagger App</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new appss.
<SurveyPrompt Title="How is Blazor working for you?" />
<h1>Tagger App</h1>
@code{

60
Tagger/Pages/Login.razor Normal file
View File

@ -0,0 +1,60 @@
@page "/login"
@inject UserManager Users;
@inject IHttpContextAccessor httpContextAccessor;
@inject NavigationManager NavManager;
@inject IHostEnvironmentAuthenticationStateProvider HostAuthentication;
@inject AuthenticationStateProvider AuthenticationStateProvider;
<h3>login</h3>
@if(LoggedIn)
{
<Button @onclick=Logout>Déconnexion</Button>
}
else
{
<InputText @bind-Value=Username placeholder="Identifiant" />
<InputText @bind-Value=Password type="password" placeholder="Mot de passe"/>
<Button @onclick=TryLogin>Connexion</Button>
}
@code {
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public bool LoggedIn { get; set; } = false;
protected async override Task OnInitializedAsync()
{
LoggedIn = await Users.IsLogged();
await base.OnInitializedAsync();
}
protected async override Task OnAfterRenderAsync(bool firstRender)
{
//if(firstRender)
await base.OnAfterRenderAsync(firstRender);
}
public async Task TryLogin()
{
if (await Users.TryLogin(Username, Password))
{
LoggedIn = await Users.IsLogged();
StateHasChanged();
return;
}
}
public async Task Logout()
{
await Users.Logout();
LoggedIn = await Users.IsLogged();
StateHasChanged();
}
}

View File

@ -1,6 +1,9 @@
@page "/page/{*Route}"
@inject PageProvider Pages;
@inject UserManager Users;
@inject NavigationManager NavManager;
@inject IJSRuntime JS
@if (CurrentPage is null)
{
@ -11,28 +14,29 @@ else
@((MarkupString)markdownHtml)
<hr />
<div hidden=@(!Editing)>
<div hidden=@(!LoggedIn || !Editing)>
<MarkdownEditor @bind-Value="@markdownValue" ValueHTMLChanged="@OnMarkdownValueHTMLChanged" />
<button @onclick="Save">Sauvegarder</button>
<button @onclick="Delete">Supprimer</button>
</div>
<div hidden=@Editing>
<div hidden=@(!LoggedIn ||Editing)>
<button @onclick="() => {Editing = true;}">Editer</button>
<button>Renommer</button>
<button @onclick=Rename>Renommer</button>
</div>
}
@code {
[Parameter]
public string? Route { get; set; }
public bool LoggedIn { get; set; }
string markdownValue = "";
string markdownHtml ="";
bool Editing = false;
ElementReference Editor;
public MarkdownPage? CurrentPage { get; set; }
@ -49,6 +53,7 @@ else
return;
markdownValue = CurrentPage.Content;
LoggedIn = await Users.IsLogged();
StateHasChanged();
}
@ -67,6 +72,21 @@ else
Pages.Save(CurrentPage);
}
async Task Rename()
{
if (CurrentPage is null)
return;
var val = await JS.InvokeAsync<string>("prompt", new[] { "Veuillez indiquer le nouveau chemin de la page" });
if (val is null)
return;
Pages.Delete(CurrentPage.Title);
CurrentPage.Title = val;
Pages.Save(CurrentPage);
NavManager.NavigateTo("/page");
}
void Delete()
{
if (CurrentPage is null)

View File

@ -1,31 +1,63 @@
@page "/page"
@inject PageProvider Pages;
@inject UserManager Users;
@inject IJSRuntime JS
@if (Paths is null)
@if(!IsLogged)
{
<p>Identification requise</p>
}
else if (Paths is null)
{
<p>Chargement...</p>
}
else
{
@foreach(var p in Paths)
@if(IsLogged)
{
<a href="/page/@p">@p</a>
}
<button @onclick=CreatePage >Ajouter une page</button>
}
<button>Ajouter une page</button>
<ul>
@foreach (var p in Paths)
{
<li><a href="/page/@p">@p</a></li>
}
</ul>
}
@code {
public string[]? Paths { get; set; } = null;
public bool IsLogged = false;
protected override void OnInitialized()
protected override async void OnInitialized()
{
Paths = Pages.List().ToArray();
base.OnInitialized();
await Flush();
}
public async Task Flush()
{
Paths = Pages.List().ToArray();
IsLogged = await Users.IsLogged();
StateHasChanged();
}
public async Task CreatePage()
{
var val = await JS.InvokeAsync<string>("prompt", new[] { "Veuillez indiquer chemin de la page" });
if (val is null)
return;
Pages.Save(new MarkdownPage
{
Title = val,
Content = ""
});
await Flush();
}
}

View File

@ -1,13 +1,36 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Tagger.Service;
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJsonFile("config.json", true);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<PageProvider>();
builder.Services.AddScoped<UserManager>();
builder.Services.AddScoped<IHostEnvironmentAuthenticationStateProvider>(sp =>
(ServerAuthenticationStateProvider)sp.GetRequiredService<AuthenticationStateProvider>()
);
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = "auth_token";
options.LoginPath = "/login";
options.Cookie.MaxAge = TimeSpan.FromHours(1);
});
builder.Services.AddAuthorization();
builder.Services.AddCascadingAuthenticationState();
var app = builder.Build();

View File

View File

@ -0,0 +1 @@
# turlutu

View File

@ -0,0 +1,38 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.Security.Claims;
namespace Tagger.Service;
public class CustomAuthStateProvider : AuthenticationStateProvider
{
private readonly ProtectedSessionStorage SessionStorage;
private ClaimsPrincipal Anonymous = new(new ClaimsIdentity());
public CustomAuthStateProvider(ProtectedSessionStorage sessionStorage)
{
SessionStorage = sessionStorage;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var userSessionStorageResult = await SessionStorage.GetAsync<User>(nameof(User));
if(!userSessionStorageResult.Success || userSessionStorageResult.Value is null)
return new AuthenticationState(Anonymous);
var claimPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Name, userSessionStorageResult.Value.Name)
}));
return new(claimPrincipal);
}
public async Task SetSession(User? user)
{
if(user is null)
await SessionStorage.DeleteAsync(nameof(User));
else
await SessionStorage.SetAsync(nameof(User), user);
}
}

View File

@ -1,14 +1,166 @@
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Authentication;
using System.Net.NetworkInformation;
using Tagger.Pages;
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.ComponentModel;
namespace Tagger.Service;
public class UserManager
{
public async Task<bool> IsLogged(AuthenticationStateProvider stateProvider)
Configuration Conf;
ProtectedLocalStorage ProtectedLocalStorage;
public UserManager(IConfiguration configuration, ProtectedLocalStorage storage)
{
var authState = await stateProvider.GetAuthenticationStateAsync();
var user = authState.User;
return (user.Identity is not null && user.Identity.IsAuthenticated);
Conf = configuration.Get<Configuration>() ?? new();
ProtectedLocalStorage = storage;
}
public async Task<User?> CurrentUser()
{
ProtectedBrowserStorageResult<User> ret;
try
{
ret = await ProtectedLocalStorage.GetAsync<User>("logged");
}
catch(InvalidOperationException ex) // statically rendered, not logged :)
{
return null;
}
if (!ret.Success)
return null;
return ret.Value;
}
public async Task<bool> IsLogged()
{
return await CurrentUser() is not null;
}
public async Task Logout()
{
await ProtectedLocalStorage.DeleteAsync("logged");
}
public async Task Login(User user)
{
await ProtectedLocalStorage.SetAsync("logged", user);
}
public async Task<bool> TryLogin(string username, string password)
{
await Logout();
foreach (var user in Conf.Users)
if (user.Name.ToLower() == username.ToLower())
{
if (user.Password != password)
return false;
await Login(user);
return true;
}
return false;
}
/*public bool IsLogged(HttpContext state)
{
if (state.User is null)
return false;
if (state.User.Identity is null)
return false;
return state.User.Identity.IsAuthenticated;
}
public async Task<bool> IsLogged(AuthenticationStateProvider state)
{
AuthenticationState authState = await state.GetAuthenticationStateAsync();
if (authState.User is null)
return false;
if (authState.User.Identity is null)
return false;
return authState.User.Identity.IsAuthenticated;
}
public async Task<bool> TryLogin(HttpContext state, string username, string password)
{
//await Logout(state);
foreach(var user in Conf.Users)
if(user.Name.ToLower() == username.ToLower())
{
if (user.Password != password)
return false;
await Login(state, user);
return true;
}
return false;
}
public async Task<bool> TryLogin(IHostEnvironmentAuthenticationStateProvider state, string username, string password)
{
//todo: Logout
foreach (var user in Conf.Users)
if (user.Name.ToLower() == username.ToLower())
{
if (user.Password != password)
return false;
await Login(state, user);
return true;
}
return false;
}
public async Task Login(IHostEnvironmentAuthenticationStateProvider state, User user)
{
var claims = new ClaimsPrincipal(new ClaimsIdentity[]
{
new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.Name, user.Name)
})
});
state.SetAuthenticationState(
Task.FromResult(
new AuthenticationState(claims)
)
);
}
private async Task Login(HttpContext state, User user)
{
await state.SignInAsync( new ClaimsPrincipal(new ClaimsIdentity[]
{
new ClaimsIdentity(new Claim[]
{
new(ClaimTypes.Name, user.Name)
})
}));
}
public async Task Logout(HttpContext state)
{
if (IsLogged(state))
await state.SignOutAsync();
}*/
}

View File

@ -14,7 +14,7 @@
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<!--<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
@ -23,11 +23,16 @@
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
</div> -->
<div class="nav-item px-3">
<NavLink class="nav-link" href="page">
<span class="oi oi-list-rich" aria-hidden="true"></span> Liste des pages
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="login">
<span class="oi oi-list-rich" aria-hidden="true"></span> Login/Compte
</NavLink>
</div>
</nav>
</div>

View File

@ -8,14 +8,18 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<Content Remove="RunEnv\config.json" />
</ItemGroup>
<ItemGroup>
<None Include="RunEnv\config.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageReference Include="PSC.Blazor.Components.Common" Version="6.0.4" />
<PackageReference Include="PSC.Blazor.Components.MarkdownEditor" Version="8.0.4" />
</ItemGroup>
<ItemGroup>
<Folder Include="RunEnv\" />
</ItemGroup>
</Project>