added basic functionalities (login, create/del/rename pages)
This commit is contained in:
parent
d531454166
commit
5707f5a2d4
12
Tagger/Configuration.cs
Normal file
12
Tagger/Configuration.cs
Normal 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();
|
||||
}
|
||||
@ -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
60
Tagger/Pages/Login.razor
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
|
||||
0
Tagger/RunEnv/Pages/Tulipe
Normal file
0
Tagger/RunEnv/Pages/Tulipe
Normal file
1
Tagger/RunEnv/Pages/pommier
Normal file
1
Tagger/RunEnv/Pages/pommier
Normal file
@ -0,0 +1 @@
|
||||
# turlutu
|
||||
38
Tagger/Service/CustomAuthStateProvider.cs
Normal file
38
Tagger/Service/CustomAuthStateProvider.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user