Started creating the docs
This commit is contained in:
4
.idea/.idea.HopFrame/.idea/indexLayout.xml
generated
4
.idea/.idea.HopFrame/.idea/indexLayout.xml
generated
@@ -1,7 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="UserContentModel">
|
<component name="UserContentModel">
|
||||||
<attachedFolders />
|
<attachedFolders>
|
||||||
|
<Path>docs</Path>
|
||||||
|
</attachedFolders>
|
||||||
<explicitIncludes />
|
<explicitIncludes />
|
||||||
<explicitExcludes />
|
<explicitExcludes />
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ public class DatabaseContext : HopDbContextBase {
|
|||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
|
||||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\DatabaseTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
namespace HopFrame.Api.Models;
|
namespace HopFrame.Api.Models;
|
||||||
|
|
||||||
public struct SingleValueResult<T>(T value) {
|
public struct SingleValueResult<TValue>(TValue value) {
|
||||||
public T Value { get; set; } = value;
|
public TValue Value { get; set; } = value;
|
||||||
|
|
||||||
public static implicit operator T(SingleValueResult<T> v) {
|
public static implicit operator TValue(SingleValueResult<TValue> v) {
|
||||||
return v.Value;
|
return v.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator SingleValueResult<T>(T v) {
|
public static implicit operator SingleValueResult<TValue>(TValue v) {
|
||||||
return new SingleValueResult<T>(v);
|
return new SingleValueResult<TValue>(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,4 +24,6 @@ public interface IUserService {
|
|||||||
Task DeleteUser(User user);
|
Task DeleteUser(User user);
|
||||||
|
|
||||||
Task<bool> CheckUserPassword(User user, string password);
|
Task<bool> CheckUserPassword(User user, string password);
|
||||||
|
|
||||||
|
Task ChangePassword(User user, string password);
|
||||||
}
|
}
|
||||||
@@ -113,4 +113,16 @@ internal sealed class UserService<TDbContext>(TDbContext context) : IUserService
|
|||||||
|
|
||||||
return entry.Password == hash;
|
return entry.Password == hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ChangePassword(User user, string password) {
|
||||||
|
var entry = await context.Users
|
||||||
|
.Where(entry => entry.Id == user.Id.ToString())
|
||||||
|
.SingleOrDefaultAsync();
|
||||||
|
|
||||||
|
if (entry is null) return;
|
||||||
|
|
||||||
|
var hash = EncryptionManager.Hash(password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
||||||
|
entry.Password = hash;
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
@switch (Type) {
|
||||||
|
case HopIcon.Reload:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
|
||||||
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.ArrowUp:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.ArrowDown:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.Cross:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.User:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.Group:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HopIcon.Logout:
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="@GetClass()" viewBox="0 0 16 16">
|
||||||
|
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v2a.5.5 0 0 1-1 0v-2A1.5 1.5 0 0 1 6.5 2h8A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 5 12.5v-2a.5.5 0 0 1 1 0z"/>
|
||||||
|
<path fill-rule="evenodd" d="M.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L1.707 7.5H10.5a.5.5 0 0 1 0 1H1.707l2.147 2.146a.5.5 0 0 1-.708.708z"/>
|
||||||
|
</svg>
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
svg.bi-nav {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
top: -1px;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public HopIcon Type { get; set; }
|
||||||
|
[Parameter] public bool NavIcon { get; set; }
|
||||||
|
|
||||||
|
public enum HopIcon {
|
||||||
|
Reload,
|
||||||
|
ArrowUp,
|
||||||
|
ArrowDown,
|
||||||
|
User,
|
||||||
|
Group,
|
||||||
|
Logout,
|
||||||
|
Cross
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetClass() {
|
||||||
|
return NavIcon ? "bi-nav" : "bi";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,20 +21,20 @@ main {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-row ::deep a:first-child {
|
.top-row ::deep a:first-child {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 640.98px) {
|
@media (max-width: 640.98px) {
|
||||||
.top-row {
|
.top-row {
|
||||||
@@ -88,9 +88,9 @@ main {
|
|||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#blazor-error-ui .dismiss {
|
#blazor-error-ui .dismiss {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
top: 0.5rem;
|
top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
@using Microsoft.AspNetCore.Components.Routing
|
@using Microsoft.AspNetCore.Components.Routing
|
||||||
|
@using HopFrame.Web.Pages.Administration.Components
|
||||||
|
|
||||||
<div class="top-row ps-3 navbar navbar-dark">
|
<div class="top-row ps-3 navbar navbar-dark">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="">HopFrame</a>
|
<a class="navbar-brand" href="/administration">HopFrame</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -11,26 +13,19 @@
|
|||||||
<nav class="flex-column" style="display: flex; height: 100%">
|
<nav class="flex-column" style="display: flex; height: 100%">
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="administration/users">
|
<NavLink class="nav-link" href="administration/users">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.User" NavIcon="true"/> Users
|
||||||
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6"/>
|
|
||||||
</svg> Users
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav-item px-3">
|
<div class="nav-item px-3">
|
||||||
<NavLink class="nav-link" href="administration/groups">
|
<NavLink class="nav-link" href="administration/groups">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-people-fill" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.Group" NavIcon="true"/> Groups
|
||||||
<path d="M7 14s-1 0-1-1 1-4 5-4 5 3 5 4-1 1-1 1zm4-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6m-5.784 6A2.24 2.24 0 0 1 5 13c0-1.355.68-2.75 1.936-3.72A6.3 6.3 0 0 0 5 9c-4 0-5 3-5 4s1 1 1 1zM4.5 8a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5"/>
|
|
||||||
</svg> Groups
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="nav-item px-3" style="margin-top: auto">
|
<div class="nav-item px-3" style="margin-top: auto">
|
||||||
<NavLink class="nav-link" href="login">
|
<NavLink class="nav-link" href="login">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-left" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.Logout" NavIcon="true"/> Logout
|
||||||
<path fill-rule="evenodd" d="M6 12.5a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5v-9a.5.5 0 0 0-.5-.5h-8a.5.5 0 0 0-.5.5v2a.5.5 0 0 1-1 0v-2A1.5 1.5 0 0 1 6.5 2h8A1.5 1.5 0 0 1 16 3.5v9a1.5 1.5 0 0 1-1.5 1.5h-8A1.5 1.5 0 0 1 5 12.5v-2a.5.5 0 0 1 1 0z"/>
|
|
||||||
<path fill-rule="evenodd" d="M.146 8.354a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L1.707 7.5H10.5a.5.5 0 0 1 0 1H1.707l2.147 2.146a.5.5 0 0 1-.708.708z"/>
|
|
||||||
</svg> Logout
|
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
.top-row {
|
.top-row {
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
background-color: rgba(0,0,0,0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
@@ -34,32 +34,20 @@
|
|||||||
background-size: cover;
|
background-size: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bi-house-door-fill-nav-menu {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-plus-square-fill-nav-menu {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.bi-list-nested-nav-menu {
|
|
||||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
.nav-item {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
padding-bottom: 0.5rem;
|
padding-bottom: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:first-of-type {
|
.nav-item:first-of-type {
|
||||||
padding-top: 1rem;
|
padding-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item:last-of-type {
|
.nav-item:last-of-type {
|
||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep .nav-link {
|
.nav-item ::deep .nav-link {
|
||||||
color: #d7d7d7;
|
color: #d7d7d7;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -69,15 +57,15 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
line-height: 3rem;
|
line-height: 3rem;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep a.active {
|
.nav-item ::deep a.active {
|
||||||
background-color: rgba(255,255,255,0.37);
|
background-color: rgba(255, 255, 255, 0.37);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item ::deep .nav-link:hover {
|
.nav-item ::deep .nav-link:hover {
|
||||||
background-color: rgba(255,255,255,0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
@using Microsoft.AspNetCore.Components.Forms
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
@using HopFrame.Web.Components
|
@using HopFrame.Web.Components
|
||||||
|
@using HopFrame.Web.Pages.Administration.Components
|
||||||
|
|
||||||
@layout AdminLayout
|
@layout AdminLayout
|
||||||
@rendermode InteractiveServer
|
@rendermode InteractiveServer
|
||||||
@@ -34,6 +35,10 @@
|
|||||||
<InputText type="text" class="form-control" id="username" required @bind-Value="User.Username"/>
|
<InputText type="text" class="form-control" id="username" required @bind-Value="User.Username"/>
|
||||||
<ValidationMessage For="() => User.Username"/>
|
<ValidationMessage For="() => User.Username"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">Password</label>
|
||||||
|
<InputText type="password" class="form-control" id="password" required @bind-Value="_password"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="groups" class="form-label">Groups</label>
|
<label for="groups" class="form-label">Groups</label>
|
||||||
@@ -43,9 +48,7 @@
|
|||||||
@foreach (var group in _groups) {
|
@foreach (var group in _groups) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemoveGroup(group)">
|
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemoveGroup(group)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.Cross"/>
|
||||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span>@group.Name.Replace("group.", "")</span>
|
<span>@group.Name.Replace("group.", "")</span>
|
||||||
@@ -78,9 +81,7 @@
|
|||||||
@foreach (var perm in User.Permissions.Where(perm => !perm.PermissionName.StartsWith("group."))) {
|
@foreach (var perm in User.Permissions.Where(perm => !perm.PermissionName.StartsWith("group."))) {
|
||||||
<li class="list-group-item">
|
<li class="list-group-item">
|
||||||
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemovePermission(perm)">
|
<button type="button" class="btn btn-danger btn-sm" style="margin-right: 15px" @onclick="() => RemovePermission(perm)">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-lg" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.Cross"/>
|
||||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8z"/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<span>@perm.PermissionName</span>
|
<span>@perm.PermissionName</span>
|
||||||
@@ -119,6 +120,7 @@
|
|||||||
private IList<PermissionGroup> _allGroups = new List<PermissionGroup>();
|
private IList<PermissionGroup> _allGroups = new List<PermissionGroup>();
|
||||||
private string _selectedGroup;
|
private string _selectedGroup;
|
||||||
private string _permissionToAdd;
|
private string _permissionToAdd;
|
||||||
|
private string _password;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
if (Guid.TryParse(UserId, out var guid)) {
|
if (Guid.TryParse(UserId, out var guid)) {
|
||||||
@@ -165,6 +167,10 @@
|
|||||||
if (result.IsConfirmed) {
|
if (result.IsConfirmed) {
|
||||||
await Users.UpdateUser(User);
|
await Users.UpdateUser(User);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_password)) {
|
||||||
|
await Users.ChangePassword(User, _password);
|
||||||
|
}
|
||||||
|
|
||||||
await Alerts.FireAsync(new SweetAlertOptions {
|
await Alerts.FireAsync(new SweetAlertOptions {
|
||||||
Title = "User edited!",
|
Title = "User edited!",
|
||||||
Icon = SweetAlertIcon.Success,
|
Icon = SweetAlertIcon.Success,
|
||||||
|
|||||||
@@ -16,13 +16,10 @@
|
|||||||
<AuthorizedView Permission="@AdminPermissions.ViewUsers" RedirectIfUnauthorized="login?redirect=/administration/users"/>
|
<AuthorizedView Permission="@AdminPermissions.ViewUsers" RedirectIfUnauthorized="login?redirect=/administration/users"/>
|
||||||
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<h3 style="user-select: none">
|
<h3>
|
||||||
Users administration
|
Users administration
|
||||||
<span style="cursor: pointer" @onclick="Reload">
|
<span class="reload" @onclick="Reload">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
<HopIconDisplay Type="HopIconDisplay.HopIcon.Reload"/>
|
||||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
|
|
||||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -36,63 +33,36 @@
|
|||||||
<table class="table table-stripped table-hover">
|
<table class="table table-stripped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" style="user-select: none">#</th>
|
<th scope="col">#</th>
|
||||||
<th scope="col" style="user-select: none">
|
<th scope="col">
|
||||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Email)">E-Mail</span>
|
<span class="sorter" @onclick="() => OrderBy(OrderType.Email)">E-Mail</span>
|
||||||
@if (_currentOrder == OrderType.Email) {
|
@if (_currentOrder == OrderType.Email) {
|
||||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" style="user-select: none">
|
<th scope="col">
|
||||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Username)">Username</span>
|
<span class="sorter" @onclick="() => OrderBy(OrderType.Username)">Username</span>
|
||||||
@if (_currentOrder == OrderType.Username) {
|
@if (_currentOrder == OrderType.Username) {
|
||||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" style="user-select: none">
|
<th scope="col">
|
||||||
<span style="cursor: pointer;" @onclick="() => OrderBy(OrderType.Registered)">Registered</span>
|
<span class="sorter" @onclick="() => OrderBy(OrderType.Registered)">Registered</span>
|
||||||
@if (_currentOrder == OrderType.Registered) {
|
@if (_currentOrder == OrderType.Registered) {
|
||||||
@if (_currentOrderDirection == OrderDirection.Asc) {
|
<HopIconDisplay Type="_currentOrderDirection == OrderDirection.Desc ? HopIconDisplay.HopIcon.ArrowDown : HopIconDisplay.HopIcon.ArrowUp"/>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-up-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z"/>
|
|
||||||
</svg>
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" style="user-select: none">Primary Group</th>
|
<th scope="col">Primary Group</th>
|
||||||
|
|
||||||
@if (_hasEditPrivileges || _hasDeletePrivileges) {
|
@if (_hasEditPrivileges || _hasDeletePrivileges) {
|
||||||
<th scope="col" style="user-select: none">Actions</th>
|
<th scope="col">Actions</th>
|
||||||
}
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var user in _users) {
|
@foreach (var user in _users) {
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">@user.Id</th>
|
<td class="bold">@user.Id</td>
|
||||||
<td>@user.Email</td>
|
<td>@user.Email</td>
|
||||||
<td>@user.Username</td>
|
<td>@user.Username</td>
|
||||||
<td>@user.CreatedAt</td>
|
<td>@user.CreatedAt</td>
|
||||||
|
|||||||
@@ -3,3 +3,15 @@
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th, h3 {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload, .sorter {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,8 +13,12 @@ public static class ServiceCollectionExtensions {
|
|||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
||||||
services.AddTransient<AuthMiddleware>();
|
services.AddTransient<AuthMiddleware>();
|
||||||
|
|
||||||
|
// Component library's
|
||||||
services.AddSweetAlert2();
|
services.AddSweetAlert2();
|
||||||
|
|
||||||
|
//TODO: Use https://blazorstrap.io/V5/V5
|
||||||
|
|
||||||
services.AddHopFrameAuthentication<TDbContext>();
|
services.AddHopFrameAuthentication<TDbContext>();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Database", "HopFra
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{58703056-8DAD-4221-BBE3-42425D2F4929}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{58703056-8DAD-4221-BBE3-42425D2F4929}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DatabaseTest", "DatabaseTest\DatabaseTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApiTest", "RestApiTest\RestApiTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Security", "HopFrame.Security\HopFrame.Security.csproj", "{7F82E1C6-4A42-4337-9E03-2EE6429D004F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Security", "HopFrame.Security\HopFrame.Security.csproj", "{7F82E1C6-4A42-4337-9E03-2EE6429D004F}"
|
||||||
EndProject
|
EndProject
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using HopFrame.Security.Authorization;
|
|||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace DatabaseTest.Controllers;
|
namespace RestApiTest.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("test")]
|
[Route("test")]
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using HopFrame.Database;
|
using HopFrame.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace DatabaseTest;
|
namespace RestApiTest;
|
||||||
|
|
||||||
public class DatabaseContext : HopDbContextBase {
|
public class DatabaseContext : HopDbContextBase {
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
|
||||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\DatabaseTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using DatabaseTest;
|
using RestApiTest;
|
||||||
using HopFrame.Api.Extensions;
|
using HopFrame.Api.Extensions;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
34
docs/Diagrams/Models/ApiModels.puml
Normal file
34
docs/Diagrams/Models/ApiModels.puml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
@startuml ApiModels
|
||||||
|
|
||||||
|
namespace HopFrame.Security {
|
||||||
|
class UserLogin {
|
||||||
|
+Email: string
|
||||||
|
+Password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserRegister {
|
||||||
|
+Username: string
|
||||||
|
+Email: string
|
||||||
|
+Password: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace HopFrame.Web {
|
||||||
|
class RegisterData {
|
||||||
|
+RepeatedPassword: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace HopFrame.Api {
|
||||||
|
class SingleValueResult<TValue> {
|
||||||
|
+Value: TValue
|
||||||
|
}
|
||||||
|
|
||||||
|
class UserPasswordValidation {
|
||||||
|
+Password: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserRegister <|-- RegisterData
|
||||||
|
|
||||||
|
@enduml
|
||||||
37
docs/Diagrams/Models/BaseModels.puml
Normal file
37
docs/Diagrams/Models/BaseModels.puml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
@startuml BaseModels
|
||||||
|
set namespaceSeparator none
|
||||||
|
|
||||||
|
namespace HopFrame.Database {
|
||||||
|
class User {
|
||||||
|
+Id: Guid
|
||||||
|
+Username: string
|
||||||
|
+Email: string
|
||||||
|
+CreatedAt: DateTime
|
||||||
|
+Permissions: IList<Permission>
|
||||||
|
}
|
||||||
|
|
||||||
|
class Permission {
|
||||||
|
+Id: long
|
||||||
|
+PermissionName: string
|
||||||
|
+Owner: Guid
|
||||||
|
+GrantedAt: DateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionGroup {
|
||||||
|
+Name: string
|
||||||
|
+IsDefaultGroup: bool
|
||||||
|
+Description: string
|
||||||
|
+CreatedAt: DateTime
|
||||||
|
+Permissions: IList<Permission>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPermissionOwner {}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPermissionOwner <|-- User
|
||||||
|
IPermissionOwner <|-- PermissionGroup
|
||||||
|
|
||||||
|
User .. Permission
|
||||||
|
PermissionGroup .. Permission
|
||||||
|
|
||||||
|
@enduml
|
||||||
41
docs/Diagrams/Models/DatabaseModels.puml
Normal file
41
docs/Diagrams/Models/DatabaseModels.puml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
@startuml DatabaseModels
|
||||||
|
set namespaceSeparator none
|
||||||
|
|
||||||
|
namespace HopFrame.Database {
|
||||||
|
class UserEntry {
|
||||||
|
+Id: string
|
||||||
|
+Username: string
|
||||||
|
+Email: string
|
||||||
|
+Password: string
|
||||||
|
+CreatedAt: DateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
class TokenEntry {
|
||||||
|
{static} +RefreshTokenType: int = 0
|
||||||
|
{static} +AccessTokenType: int = 1
|
||||||
|
|
||||||
|
+Type: int
|
||||||
|
+Token: string
|
||||||
|
+UserId: string
|
||||||
|
+CreatedAt: DateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionEntry {
|
||||||
|
+RecordId: long
|
||||||
|
+PermissionText: string
|
||||||
|
+UserId: string
|
||||||
|
+GrantedAt: DateTime
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupEntry {
|
||||||
|
+Name: string
|
||||||
|
+Default: bool
|
||||||
|
+Description: string
|
||||||
|
+CreatedAt: DateTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserEntry *-- TokenEntry
|
||||||
|
UserEntry *-- PermissionEntry
|
||||||
|
|
||||||
|
@enduml
|
||||||
21
docs/Diagrams/Models/README.md
Normal file
21
docs/Diagrams/Models/README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Models for HopFrame
|
||||||
|
|
||||||
|
This page shows all models that HopFrame uses.
|
||||||
|
|
||||||
|
|
||||||
|
## Base Models
|
||||||
|
These are the models used by the various database services.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## API Models
|
||||||
|
These are the models used by the REST API and the Blazor API.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Database Models
|
||||||
|
These are the models that correspond to the scheme in the Database
|
||||||
|
|
||||||
|

|
||||||
1
docs/Diagrams/Models/img/ApiModels.svg
Normal file
1
docs/Diagrams/Models/img/ApiModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
1
docs/Diagrams/Models/img/BaseModels.svg
Normal file
1
docs/Diagrams/Models/img/BaseModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
1
docs/Diagrams/Models/img/DatabaseModels.svg
Normal file
1
docs/Diagrams/Models/img/DatabaseModels.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
Reference in New Issue
Block a user