Merge pull request #142 from LD-Reborn/141-feature-implement-proper-authorization-with-groups

141 feature implement proper authorization with groups
This commit is contained in:
LD50
2025-10-17 18:52:58 +02:00
committed by GitHub
9 changed files with 120 additions and 28 deletions

View File

@@ -20,6 +20,7 @@ public class AssetsController : Controller
_logger = logger; _logger = logger;
} }
[Authorize(Roles = "CanManageAssets,CanInventorize")]
[HttpGet("Get")] [HttpGet("Get")]
public async Task<AssetsGetResponseModel> GetAllAssetModelAsync(string Cn) public async Task<AssetsGetResponseModel> GetAllAssetModelAsync(string Cn)
{ {
@@ -41,6 +42,7 @@ public class AssetsController : Controller
return result; return result;
} }
[Authorize(Roles = "CanManageAssets")]
[HttpGet("GetAll")] [HttpGet("GetAll")]
public async Task<AssetsGetAllResponseModel> GetAllAssetModelAsync() public async Task<AssetsGetAllResponseModel> GetAllAssetModelAsync()
{ {
@@ -63,6 +65,7 @@ public class AssetsController : Controller
return result; return result;
} }
[Authorize(Roles = "CanManageAssets")]
[HttpPost("Create")] [HttpPost("Create")]
public async Task<AssetsCreateResponseModel> Create([FromBody]AssetsCreateRequestModel assetModel) public async Task<AssetsCreateResponseModel> Create([FromBody]AssetsCreateRequestModel assetModel)
{ {
@@ -117,6 +120,7 @@ public class AssetsController : Controller
return result; return result;
} }
[Authorize(Roles = "CanManageAssets")]
[HttpDelete("Delete")] [HttpDelete("Delete")]
public async Task<AssetsDeleteResponseModel> Delete([BindRequired] string cn) public async Task<AssetsDeleteResponseModel> Delete([BindRequired] string cn)
{ {
@@ -143,6 +147,7 @@ public class AssetsController : Controller
}); });
} }
[Authorize(Roles = "CanManageAssets,CanInventorize")]
[HttpPatch("Update")] [HttpPatch("Update")]
public async Task<AssetsUpdateResponseModel> Update([FromBody] AssetsModifyRequestModel requestModel) public async Task<AssetsUpdateResponseModel> Update([FromBody] AssetsModifyRequestModel requestModel)
{ {

View File

@@ -5,7 +5,7 @@ using Berufsschule_HAM.Models;
using System.Text.Json; using System.Text.Json;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
[Authorize] [Authorize(Roles = "CanManageGroups")]
[Route("[controller]")] [Route("[controller]")]
public class GroupsController : Controller public class GroupsController : Controller
{ {

View File

@@ -27,7 +27,7 @@ public class HomeController : Controller
return View(); return View();
} }
[Authorize] [Authorize(Roles = "CanManageAssets")]
[HttpGet("Assets")] [HttpGet("Assets")]
public async Task<IActionResult> Assets() public async Task<IActionResult> Assets()
{ {
@@ -48,14 +48,14 @@ public class HomeController : Controller
return View(new HomeIndexViewModel() { AssetsTableViewModels = assetsTableViewModels }); return View(new HomeIndexViewModel() { AssetsTableViewModels = assetsTableViewModels });
} }
[Authorize] [Authorize(Roles = "CanInventorize")]
[HttpGet("Inventory")] [HttpGet("Inventory")]
public ActionResult Inventory() public ActionResult Inventory()
{ {
return View(); return View();
} }
[Authorize] [Authorize(Roles = "CanManageLocations")]
[HttpGet("Locations")] [HttpGet("Locations")]
public async Task<ActionResult> LocationsAsync() public async Task<ActionResult> LocationsAsync()
{ {
@@ -74,7 +74,7 @@ public class HomeController : Controller
return View(new LocationsIndexViewModel() { LocationTableViewModels = LocationsTableViewModels }); return View(new LocationsIndexViewModel() { LocationTableViewModels = LocationsTableViewModels });
} }
[Authorize] [Authorize(Roles = "CanManageUsers")]
[HttpGet("Users")] [HttpGet("Users")]
public async Task<ActionResult> UsersAsync() public async Task<ActionResult> UsersAsync()
{ {
@@ -95,7 +95,7 @@ public class HomeController : Controller
return View(new UsersIndexViewModel() { UserTableViewModels = UserTableViewModels }); return View(new UsersIndexViewModel() { UserTableViewModels = UserTableViewModels });
} }
[Authorize] [Authorize(Roles = "CanManageGroups")]
[HttpGet("Groups")] [HttpGet("Groups")]
public async Task<ActionResult> GroupsAsync() public async Task<ActionResult> GroupsAsync()
{ {
@@ -113,6 +113,19 @@ public class HomeController : Controller
[ [
new(ClaimTypes.Name, username) new(ClaimTypes.Name, username)
]; ];
HashSet<string> roles = [];
foreach (string groupCn in authenticationResult.UserModel?.Description?.Groups ?? [])
{
GroupModel group = await _ldap.GetGroupByCnAsync(groupCn, _ldap.GroupsAttributes);
foreach (GroupPermission permission in group.Permissions)
{
roles.Add(permission.ToString());
}
}
foreach (string role in roles)
{
claims.Add(new(ClaimTypes.Role, role));
}
var claimsIdentity = new ClaimsIdentity( var claimsIdentity = new ClaimsIdentity(
claims, claims,

View File

@@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Authorization;
using Novell.Directory.Ldap; using Novell.Directory.Ldap;
using Berufsschule_HAM.Helpers; using Berufsschule_HAM.Helpers;
[Authorize] [Authorize(Roles = "CanManageLocations")]
[Route("[controller]")] [Route("[controller]")]
public class LocationsController : Controller public class LocationsController : Controller
{ {

View File

@@ -7,8 +7,9 @@ using Berufsschule_HAM.Helpers;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Text.Json;
[Authorize] [Authorize(Roles = "CanManageUsers")]
[Route("[controller]")] [Route("[controller]")]
public class UsersController : Controller public class UsersController : Controller
{ {
@@ -145,4 +146,47 @@ public class UsersController : Controller
} }
return true; return true;
} }
[HttpPost("AddGroup")]
public async Task<bool> AddGroup([FromBody]UsersAddGroupRequestModel requestModel)
{
try
{
UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid);
userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" };
userModel.Description.Groups ??= [];
try
{
GroupModel group = await _ldap.GetGroupByCnAsync(requestModel.GroupUid, _ldap.GroupsAttributes);
} catch (Exception)
{
return false;
}
userModel.Description.Groups.Add(requestModel.GroupUid);
await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description));
return true;
} catch (Exception ex)
{
_logger.LogError("Unable to add group {} to user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]);
return false;
}
}
[HttpPost("RemoveGroup")]
public async Task<bool> RemoveGroup([FromBody]UsersRemoveGroupRequestModel requestModel)
{
try
{
UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid);
userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" };
userModel.Description.Groups ??= [];
userModel.Description.Groups.Remove(requestModel.GroupUid);
await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description));
return true;
} catch (Exception ex)
{
_logger.LogError("Unable to remove group {} from user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]);
return false;
}
}
} }

View File

@@ -29,6 +29,7 @@ public class UserDescription
public required string BirthDate { get; set; } public required string BirthDate { get; set; }
public required UserAddress Address { get; set; } public required UserAddress Address { get; set; }
public required string Workplace { get; set; } public required string Workplace { get; set; }
public List<string>? Groups { get; set; }
} }
public class UserAddress public class UserAddress
@@ -40,8 +41,9 @@ public class UserAddress
public class UserAuthenticationResult public class UserAuthenticationResult
{ {
public required bool Success; public required bool Success { get; set; }
public UserNotAuthenticatedReason AuthenticationState { get; set; } = UserNotAuthenticatedReason.None; public UserNotAuthenticatedReason AuthenticationState { get; set; } = UserNotAuthenticatedReason.None;
public UserModel? UserModel { get; set; }
} }
public enum UserNotAuthenticatedReason public enum UserNotAuthenticatedReason

View File

@@ -29,3 +29,15 @@ public class UsersDeleteRequestModel(bool successful, string exception = "None")
public string? Exception { get; set; } = exception; public string? Exception { get; set; } = exception;
} }
public class UsersAddGroupRequestModel
{
public required string Uid { get; set; }
public required string GroupUid { get; set; }
}
public class UsersRemoveGroupRequestModel
{
public required string Uid { get; set; }
public required string GroupUid { get; set; }
}

View File

@@ -297,7 +297,7 @@ public async Task CreateAsset(LdapAttributeSet attributeSet)
} }
if (CompareStringToSha256(password, user.UserPassword)) if (CompareStringToSha256(password, user.UserPassword))
{ {
return new() { Success = true }; return new() { Success = true, UserModel = user };
} }
return new() { Success = false, AuthenticationState = UserNotAuthenticatedReason.InvalidCredentials }; return new() { Success = false, AuthenticationState = UserNotAuthenticatedReason.InvalidCredentials };
} }

View File

@@ -1,4 +1,5 @@
@using Microsoft.AspNetCore.Mvc.Localization @using Microsoft.AspNetCore.Mvc.Localization
@using System.Security.Claims
@inject IViewLocalizer T @inject IViewLocalizer T
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
@@ -32,24 +33,39 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Index">@T["Quick-Actions"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Index">@T["Quick-Actions"]</a>
</li> </li>
@if (User.Identity.IsAuthenticated) @if (User.Identity?.IsAuthenticated ?? false)
{
@if (User.HasClaim(ClaimTypes.Role, "CanInventorize"))
{ {
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Inventory">@T["Inventory"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Inventory">@T["Inventory"]</a>
</li> </li>
}
@if (User.HasClaim(ClaimTypes.Role, "CanManageAssets"))
{
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Assets">@T["Assets"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Assets">@T["Assets"]</a>
</li> </li>
}
@if (User.HasClaim(ClaimTypes.Role, "CanManageLocations"))
{
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Locations">@T["Locations"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Locations">@T["Locations"]</a>
</li> </li>
}
@if (User.HasClaim(ClaimTypes.Role, "CanManageUsers"))
{
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Users">@T["Users"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Users">@T["Users"]</a>
</li> </li>
}
@if (User.HasClaim(ClaimTypes.Role, "CanManageGroups"))
{
<li class="nav-item"> <li class="nav-item">
<a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Groups">@T["Groups"]</a> <a class="nav-link text" asp-area="" asp-controller="Home" asp-action="Groups">@T["Groups"]</a>
</li> </li>
} }
}
<li class="nav-item"> <li class="nav-item">
@if (User.Identity.IsAuthenticated) @if (User.Identity.IsAuthenticated)
{ {