Compare commits

..

29 Commits

Author SHA1 Message Date
Developer 02
d1155cb406 Update project file for version 3.0.2 release
- Changed SDK declaration to include a non-breaking Unicode character.
- Incremented version number from 3.0.1 to 3.0.2 for package and assembly.
- Updated copyright year to 2024.
2025-04-16 14:29:33 +02:00
Developer 02
9ae3345859 chore: Aktualisierung des Verweises auf das Paketsymbol in den Projektdateien
Die Projektdateien für `DigitalData.UserManager` wurden geändert, um auf ein neues Paketsymbol zu verweisen, das sich unter `..\Assets\icon.png` befindet. Dazu gehört das Hinzufügen des Elements „<PackageIcon>“ in „DigitalData.UserManager.API.csproj“ und die Aktualisierung der Elemente „<None>“ in mehreren Projektdateien. Die alte Icondatei wurde gelöscht, und die Abschnitte von `<ItemGroup>` wurden angepasst, um sicherzustellen, dass das neue Icon korrekt in das Projekt gepackt wird.
2025-04-16 14:23:59 +02:00
Developer 02
a4e4883d6b chore: Hinzufügen des Projekts "Assets" mit einer Symboldatei zur Projektmappe
Es wurde ein neues Projekt namens "Assets" eingeführt, das eine Symboldatei (`icon.png`) enthält. Dieses Projekt ist in der Struktur der Projektmappe unter "Projektmappenobjekte" organisiert, was die Verwaltung der Projektressourcen verbessert.
2025-04-16 14:19:22 +02:00
Developer 02
9f6acfb8d3 chore: Erhöhen der Projektversionen und Entfernen unbenutzter JS-Dateien
Aktualisierte Versionsnummern für API-, Anwendungs- und Infrastrukturprojekte:
- API: 6.0.0 auf 6.0.1
- Anwendung: 3.1.2 bis 3.1.3
- Infrastruktur: 3.0.1 bis 3.0.2

Außerdem wurden die Assembly- und Dateiversionen entsprechend aktualisiert. Zwei unbenutzte JavaScript-Dateien wurden aus dem API-Projekt entfernt.
2025-04-16 14:16:28 +02:00
Developer 02
be78947b48 Remove Windows service check and update package versions
Updated `Program.cs` to eliminate the Windows service conditional check.
Updated `DigitalData.Core.Abstractions` and `DigitalData.Core.Application`
to versions 3.4.1 and 3.2.1, respectively. Removed the
`DigitalData.Core.Infrastructure` package reference. In
`DigitalData.UserManager.Infrastructure.csproj`, updated
`DigitalData.Core.Infrastructure` to version 2.0.1 and added
`Microsoft.EntityFrameworkCore.Relational` for net7.0, net8.0,
and net9.0 with corresponding versions.
2025-04-16 14:13:04 +02:00
Developer 02
b2e64e794c Update package references for target frameworks
Added `DigitalData.Core.Infrastructure` version `2.0.0` and updated package references for `net7.0`, `net8.0`, and `net9.0` in `DigitalData.UserManager.Application.csproj`. Updated `AutoMapper` to version `14.0.0` for `net8.0` and `net9.0`, along with other related packages to their latest versions.
2025-04-16 13:16:07 +02:00
Developer 02
d6b82cf085 Update Entity Framework Core package references
Removed the fixed version for Microsoft.EntityFrameworkCore
and added conditional references for versions 7.0.20, 8.0.15,
and 9.0.4 based on target frameworks net7.0, net8.0, and
net9.0, respectively.
2025-04-16 13:04:40 +02:00
Developer 02
6cd72fe755 Überarbeitung der UserManager-Konfiguration und -Abhängigkeiten
Unnötige Paketverweise wurden aus dem API-Projekt entfernt, einschließlich Entity Framework Core und System.DirectoryServices. Ersetzte `AddDbContext` durch `AddUserManager` in `Program.cs`, um die UserManager-Dienstkonfiguration zu rationalisieren. Die Datei `Extensions.cs` wurde aktualisiert, um die Überladung der Verbindungszeichenfolge für `AddUserManager` zu entfernen, und `DependencyInjection.cs` wurde geändert, um die Einrichtung der UserManager-Infrastruktur zu vereinfachen.
2025-04-16 12:50:36 +02:00
Developer 02
8d0beab709 Refactoring der Projektabhängigkeiten und Aktualisierung von Program.cs
Verweise auf DigitalData.UserManager.Application und
DigitalData.UserManager.Domain in DigitalData.UserManager.API.csproj entfernt.
Ein Verweis auf DigitalData.UserManager.DependencyInjection
wurde hinzugefügt, um die Architektur zu rationalisieren und die Verwaltung von Abhängigkeiten zu verbessern.
Program.cs wurde aktualisiert, um den neuen Namespace für Dependency Injection
für ein verbessertes Service-Management aufzunehmen.
2025-04-16 11:45:50 +02:00
Developer 02
52871f006d Refactor UserManager dependency injection setup
Umbenennung der Methoden für mehr Klarheit: `AddUserManager` zu `AddUserManagerApplication` und `AddInfrastructureServices` zu `AddUserManagerInfrastructure`. Neue Projektreferenzen für `Application`, `Domain` und `Infrastructure` in der `DigitalData.UserManager.DependencyInjection.csproj` hinzugefügt. Einführung von Erweiterungsmethoden in `Extensions.cs`, um die Registrierung von UserManager-Diensten zu rationalisieren und die Kohärenz zwischen Infrastruktur- und Anwendungsdienstregistrierungen zu verbessern.
2025-04-16 11:42:48 +02:00
Developer 02
ac064f4671 refactor: UserManagerPack durch DependencyInjection-Projekt ersetzen
Das Projekt `DigitalData.UserManagerPack` wurde entfernt und `DigitalData.UserManager.DependencyInjection` hinzugefügt. Die Konfiguration der Projektmappe wurde aktualisiert, um diese Änderung widerzuspiegeln und die richtigen Build-Einstellungen für den Debug- und den Release-Modus sicherzustellen. Das neue Projekt behält die gleichen Eigenschaften und Einstellungen wie das alte Projekt und erfüllt unter einem neuen Namen einen ähnlichen Zweck.
2025-04-16 11:07:06 +02:00
Developer 02
2d792c8544 chore: Refactor project structure and update DI setup
Die Datei `DIExtensions.cs` wurde erheblich überarbeitet, um Abhängigkeiten von der Schicht `DigitalData.UserManager.Infrastructure` zu entfernen. Die Methode `AddUserManager` wurde vereinfacht und eine Methode `AddEncryptor` hinzugefügt. Die Projektverweise auf die Infrastrukturebene in der Anwendungsprojektdatei wurden entfernt. Aktualisierte Servicedateien zur Verwendung neuer Repository-Schnittstellen aus „DigitalData.UserManager.Application.Contracts.Repositories“. Repository-Schnittstellen wurden in den Namensraum für Anwendungsverträge verschoben und ihre Definitionen aktualisiert. Einführung von `DependencyInjection.cs` für die Handhabung von Infrastrukturdienstregistrierungen. Aktualisierte Repository-Implementierungen, um sie an die neue Struktur anzupassen, die Trennung von Belangen zu verbessern und die Injektion von Abhängigkeiten zu vereinfachen.
2025-04-16 11:02:44 +02:00
Developer 02
239a5708a7 Add DigitalData.UserManagerPack project to solution 2025-04-16 10:26:47 +02:00
Developer 02
23283a6846 Add DigitalData.UserManagerPack project file
Created and configured `DigitalData.UserManagerPack.csproj` to target .NET frameworks 7.0, 8.0, and 9.0. Added project metadata including PackageId, Version, Authors, and Description. Established references to `DigitalData.UserManager.Application`, `DigitalData.UserManager.Domain`, and `DigitalData.UserManager.Infrastructure`.
2025-04-16 10:25:11 +02:00
Developer 02
7d317308d5 chore: Aktualisierung der Projekte auf .NET 9.0 und Paketversion
Die Projektdateien für die Anwendung DigitalData.UserManager wurden aktualisiert, um .NET 9.0 neben .NET 7.0 und .NET 8.0 als Ziel zu verwenden. Diese Änderung betrifft die folgenden Projektdateien: `DigitalData.UserManager.API.csproj`, `DigitalData.UserManager.Application.csproj`, `DigitalData.UserManager.Domain.csproj` und `DigitalData.UserManager.Infrastructure.csproj`.

Außerdem wurde die Version des Pakets `DigitalData.EmailProfilerDispatcher.Abstraction` in der Datei `DigitalData.UserManager.Application.csproj` von 2.0.0 auf 3.0.0 aktualisiert.
2025-04-16 10:13:42 +02:00
Developer 02
2aab942563 refactor(PlaceholderAuthController): AuthController umbenennen 2025-03-25 15:34:34 +01:00
Developer 02
054c91609e feat(AuthController): Aktiviere GetUserWithClaims Endpunkt 2025-03-25 14:15:31 +01:00
Developer 02
5c097eda80 Aktivierter Logout-Endpunkt 2025-03-25 14:00:19 +01:00
Developer 02
d228a3cd50 Aktiviert Endpunkt prüfen 2025-03-25 13:56:17 +01:00
Developer 02
0786013bd0 fix(AuthTokenKey): Update default audiance 2025-03-25 13:50:30 +01:00
Developer 02
beabc3f4e0 fix(appsettings): AuthCLientParms.Url 2025-03-25 13:32:24 +01:00
Developer 02
44560e7057 feat(Programm): Konfiguriert für Auth.API 2025-03-25 13:19:29 +01:00
Developer 02
2098a7d48d feat(LazyServiceProvider): Erstellt als Callback des Service Providers 2025-03-25 13:09:39 +01:00
Developer 02
140f172369 Ansprüche über ModelExtensipons konfigurieren 2025-03-25 13:07:06 +01:00
Developer 02
b3e7c1aa99 chore(API): Aktualisierung DigitalData.Auth.Client 1.3.5 2025-03-25 12:33:29 +01:00
Developer 02
0d4436b061 chore: aktualisiert, um sowohl .net 7 als auch .net 8 zu unterstützen 2025-03-25 11:52:03 +01:00
Developer 02
b88fd78367 chore(API): Hinzufügen von DigitalData.Auth.Client 1.3.3 nuget-package. 2025-03-24 16:47:56 +01:00
Developer 02
7670f2119e refactor(AuthController): Konvertiert in einen Platzhalter-Controller für Swagger. 2025-03-24 16:40:54 +01:00
Developer 02
a142196d87 chore: aktualisierte DigitalData.Core-Pakete 2025-03-24 14:17:23 +01:00
59 changed files with 1094 additions and 953 deletions

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "9.0.3",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@@ -1,158 +1,72 @@
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using DigitalData.UserManager.Application;
using DigitalData.UserManager.Application.DTOs.Auth; using DigitalData.UserManager.Application.DTOs.Auth;
using DigitalData.Core.Abstractions.Application; using DigitalData.UserManager.Application.Contracts;
using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using Microsoft.Extensions.Localization;
using DigitalData.UserManager.Application;
using System.Security.Claims;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{ {
[Route("api/[controller]")] private readonly ILogger<UserController> _logger;
public class AuthController : ControllerBase private readonly IUserService _userService;
private readonly IStringLocalizer<Resource> _localizer;
public AuthController(ILogger<UserController> logger, IUserService userService, IStringLocalizer<Resource> localizer)
{ {
private readonly IUserService _userService; _logger = logger;
private readonly IGroupOfUserService _gouService; _userService = userService;
private readonly IDirectorySearchService _dirSearchService; _localizer = localizer;
private readonly IStringLocalizer<Resource> _localizer; }
private readonly ILogger<AuthController> _logger;
private readonly IConfiguration _config; [Authorize]
public AuthController(IUserService userService, IGroupOfUserService gouService, IDirectorySearchService directorySearchService, IStringLocalizer<Resource> localizer, ILogger<AuthController> logger, IConfiguration configuration) [HttpGet("check")]
public IActionResult CheckAuthentication() => Ok();
[AllowAnonymous]
[HttpPost("login")]
public Task<IActionResult> Login([FromBody] LogInDto login) => throw new NotImplementedException();
[Authorize]
[HttpGet("user")]
public async Task<IActionResult> GetUserWithClaims()
{
try
{ {
_userService = userService; // Extract the username from the Name claim.
_gouService = gouService; string? username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
_dirSearchService = directorySearchService;
_localizer = localizer;
_logger = logger;
_config = configuration;
}
[AllowAnonymous] if (string.IsNullOrEmpty(username))
[HttpGet("check")] return Unauthorized();
public IActionResult CheckAuthentication()
{
try
{
return Ok(User.Identity?.IsAuthenticated ?? false);
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[AllowAnonymous] return await _userService.ReadByUsernameAsync(username)
[HttpPost("login")] .ThenAsync(Ok, IActionResult (m, n) =>
public async Task<IActionResult> Login([FromBody] LogInDto login)
{
try
{
bool isValid = _dirSearchService.ValidateCredentials(login.Username, login.Password);
if (!isValid)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFound]));
var allowedGroupName = _config.GetSection("AllowedGroupName").Get<string>()
?? throw new InvalidOperationException("Allowed group names configuration is missing.");
var gouMsg = await _gouService.HasGroup(login.Username, allowedGroupName, caseSensitive: false);
if (!gouMsg.IsSuccess)
return Unauthorized(Result.Fail().Message(_localizer[Key.UnauthorizedUser]));
//find the user
var uRes = await _userService.ReadByUsernameAsync(login.Username);
if (!uRes.IsSuccess || uRes.Data is null)
{ {
return Unauthorized(uRes); _logger.LogNotice(n);
} return NotFound(Result.Fail().Message(_localizer[Key.UserNotFound]));
});
UserReadDto user = uRes.Data;
// Create claims
var claims = new List<Claim>
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? ""),
new (ClaimTypes.Role, "PM_USER")
};
// Create claimsIdentity
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// Create authProperties
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
};
// Sign in
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
_dirSearchService.SetSearchRootCache(user.Username, login.Password);
return Ok();
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
} }
catch (Exception ex)
[Authorize]
[HttpGet("user")]
public async Task<IActionResult> GetUserWithClaims()
{ {
try _logger.LogError(ex, "{Message}", ex.Message);
{ return StatusCode(StatusCodes.Status500InternalServerError);
// Extract the username from the Name claim.
string? username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
if (string.IsNullOrEmpty(username))
return Unauthorized();
return await _userService.ReadByUsernameAsync(username)
.ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return NotFound(Result.Fail().Message(_localizer[Key.UserNotFound]));
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[Authorize]
[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
try
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
} }
} }
[Authorize]
[HttpPost("logout")]
public IActionResult Logout()
{
Response.Cookies.Delete("AuthToken", new()
{
Path = "/"
});
return Ok();
}
} }

View File

@@ -7,41 +7,40 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System.Security.Claims; using System.Security.Claims;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Authorize]
public class BaseAuthController<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TBaseEntity> : CRUDControllerBaseWithErrorHandling<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TBaseEntity, int>
where TCRUDService : IBaseService<TCreateDto, TReadDto, TBaseEntity>
where TCreateDto : BaseCreateDto
where TReadDto : class
where TUpdateDto : BaseUpdateDto
where TBaseEntity : BaseEntity
{ {
[Authorize] private readonly Lazy<int?> _lUserId;
public class BaseAuthController<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TBaseEntity> : CRUDControllerBaseWithErrorHandling<TCRUDService, TCreateDto, TReadDto, TUpdateDto, TBaseEntity, int>
where TCRUDService : IBaseService<TCreateDto, TReadDto, TUpdateDto, TBaseEntity> public BaseAuthController(ILogger logger, TCRUDService service, IUserService userService) : base(logger, service)
where TCreateDto : BaseCreateDto
where TReadDto : class
where TUpdateDto : BaseUpdateDto
where TBaseEntity : BaseEntity
{ {
private readonly Lazy<int?> _lUserId; _lUserId = new(() =>
public BaseAuthController(ILogger logger, TCRUDService service, IUserService userService) : base(logger, service)
{ {
_lUserId = new(() => var idSt = User.FindFirstValue(ClaimTypes.NameIdentifier);
{ bool hasId = int.TryParse(idSt, out int id);
var idSt = User.FindFirstValue(ClaimTypes.NameIdentifier); return hasId ? id : null;
bool hasId = int.TryParse(idSt, out int id); });
return hasId ? id : null;
});
service.UserFactoryAsync = async () => service.UserFactoryAsync = async () =>
{ {
var id = _lUserId.Value; var id = _lUserId.Value;
return id is int intId return id is int intId
? await userService.ReadByIdAsync(intId).ThenAsync( ? await userService.ReadByIdAsync(intId).ThenAsync(
Success: res => res, Success: res => res,
Fail: UserReadDto? (m, n) => Fail: UserReadDto? (m, n) =>
{ {
_logger.LogNotice(n); _logger.LogNotice(n);
return null; return null;
}) })
: null; : null;
}; };
}
} }
} }

View File

@@ -10,226 +10,225 @@ using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Route("api/[controller]")]
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")]
[Authorize]
public class DirectoryController : ControllerBase
{ {
[Route("api/[controller]")] private readonly IUserService _userService;
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "<Pending>")] private readonly IDirectorySearchService _dirSearchService;
[Authorize] private readonly Dictionary<string, string> _customSearchFilters;
public class DirectoryController : ControllerBase private readonly IStringLocalizer<Resource> _localizer;
private readonly ILogger<DirectoryController> _logger;
public DirectoryController(IConfiguration configuration, IStringLocalizer<Resource> localizer, IUserService userService, IDirectorySearchService directorySearchService, ILogger<DirectoryController> logger)
{ {
private readonly IUserService _userService; _localizer = localizer;
private readonly IDirectorySearchService _dirSearchService; _userService = userService;
private readonly Dictionary<string, string> _customSearchFilters; _dirSearchService = directorySearchService;
private readonly IStringLocalizer<Resource> _localizer;
private readonly ILogger<DirectoryController> _logger;
public DirectoryController(IConfiguration configuration, IStringLocalizer<Resource> localizer, IUserService userService, IDirectorySearchService directorySearchService, ILogger<DirectoryController> logger) var customSearchFiltersSection = configuration.GetSection("DirectorySearch:CustomSearchFilters");
_customSearchFilters = customSearchFiltersSection.Get<Dictionary<string, string>>() ?? new();
_logger = logger;
}
[HttpGet("Root/{username}")]
public IActionResult GetRootOf(string username)
{
try
{ {
_localizer = localizer; var root = _dirSearchService.GetSearchRootCache(username);
_userService = userService;
_dirSearchService = directorySearchService;
var customSearchFiltersSection = configuration.GetSection("DirectorySearch:CustomSearchFilters"); return root is null ? NotFound() : Ok(new
_customSearchFilters = customSearchFiltersSection.Get<Dictionary<string, string>>() ?? new(); {
_logger = logger; guid = root.Guid,
nativeGuid = root.NativeGuid,
name = root.Name,
path = root.Path,
parentPath = root.Parent?.Path,
username = root.Username,
schemaClassName = root.SchemaClassName
});
} }
catch (Exception ex)
[HttpGet("Root/{username}")]
public IActionResult GetRootOf(string username)
{ {
try _logger.LogError(ex, "{Message}", ex.Message);
{ return StatusCode(StatusCodes.Status500InternalServerError);
var root = _dirSearchService.GetSearchRootCache(username);
return root is null ? NotFound() : Ok(new
{
guid = root.Guid,
nativeGuid = root.NativeGuid,
name = root.Name,
path = root.Path,
parentPath = root.Parent?.Path,
username = root.Username,
schemaClassName = root.SchemaClassName
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("CustomSearchFilter")]
public IActionResult GetAllCustomFilters(string? filtername)
{
try
{
if (filtername is null)
{
return Ok(_customSearchFilters);
}
else
{
_dirSearchService.CustomSearchFilters.TryGetValue(filtername, out string? filter);
return filter is null ? NotFound() : Ok(filter);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("CreateSearchRoot")]
public async Task<IActionResult> CreateSearchRoot([FromBody] SearchRootCreateDto searchRootCreateDto)
{
try
{
var dirEntryUsername = searchRootCreateDto.DirEntryUsername ?? CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
bool isValid = _dirSearchService.ValidateCredentials(dirEntryUsername, searchRootCreateDto.DirEntryPassword);
if (!isValid)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFound]));
var userResult = await _userService.ReadByUsernameAsync(dirEntryUsername);
if (!userResult.IsSuccess || userResult.Data is null)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFoundInLocalDB]));
_dirSearchService.SetSearchRootCache(userResult.Data.Username, searchRootCreateDto.DirEntryPassword);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("SearchByFilter/{filter}")]
public IActionResult SearchByFilter([FromRoute] string filter, string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
return _dirSearchService.FindAllByUserCache(dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("SearchByFilterName/{filterName}")]
public IActionResult SearchByFilterName([FromRoute] string filterName, string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue(filterName, out string? filter);
if (filter is null)
return NotFound($"The filter named {filterName} does not exist.");
return _dirSearchService.FindAllByUserCache(dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Group")]
public IActionResult GetGroups(string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue("Group", out string? filter);
if (filter is null)
throw new InvalidOperationException("The LDAP Group Search filter configuration is missing in your appsettings. Please ensure it's added under DirectorySearch:CustomSearchFilters:Group to enable group searches.");
return _dirSearchService.FindAllByUserCache(username: dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("User")]
public IActionResult GetUsersByGroupName(string? dirEntryUsername, [FromQuery] string? groupName = null)
{
try
{
string[] propName = { "memberof", "samaccountname", "givenname", "sn", "mail" };
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue("User", out string? filter);
if (filter is null)
throw new InvalidOperationException("The LDAP User Search filter configuration is missing in your appsettings. Please ensure it's added under DirectorySearch:CustomSearchFilters:User to enable group searches.");
return _dirSearchService.FindAllByUserCache(username: dirEntryUsername, filter, properties: propName).Then(
Success: data =>
{
if (groupName is not null)
data = data
.Where(rp => rp.PropertyNames.Cast<string>().Contains("memberof") &&
rp["memberof"].Cast<string>().Any(ldapDir => ldapDir.Contains(groupName)))
.ToList();
return Ok(data);
},
Fail: IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
private string? CurrentUser
{
get => (HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value);
} }
} }
[HttpGet("CustomSearchFilter")]
public IActionResult GetAllCustomFilters(string? filtername)
{
try
{
if (filtername is null)
{
return Ok(_customSearchFilters);
}
else
{
_dirSearchService.CustomSearchFilters.TryGetValue(filtername, out string? filter);
return filter is null ? NotFound() : Ok(filter);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("CreateSearchRoot")]
public async Task<IActionResult> CreateSearchRoot([FromBody] SearchRootCreateDto searchRootCreateDto)
{
try
{
var dirEntryUsername = searchRootCreateDto.DirEntryUsername ?? CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
bool isValid = _dirSearchService.ValidateCredentials(dirEntryUsername, searchRootCreateDto.DirEntryPassword);
if (!isValid)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFound]));
var userResult = await _userService.ReadByUsernameAsync(dirEntryUsername);
if (!userResult.IsSuccess || userResult.Data is null)
return Unauthorized(Result.Fail().Message(_localizer[Key.UserNotFoundInLocalDB]));
_dirSearchService.SetSearchRootCache(userResult.Data.Username, searchRootCreateDto.DirEntryPassword);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("SearchByFilter/{filter}")]
public IActionResult SearchByFilter([FromRoute] string filter, string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
return _dirSearchService.FindAllByUserCache(dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("SearchByFilterName/{filterName}")]
public IActionResult SearchByFilterName([FromRoute] string filterName, string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue(filterName, out string? filter);
if (filter is null)
return NotFound($"The filter named {filterName} does not exist.");
return _dirSearchService.FindAllByUserCache(dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Group")]
public IActionResult GetGroups(string? dirEntryUsername, params string[] propName)
{
try
{
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue("Group", out string? filter);
if (filter is null)
throw new InvalidOperationException("The LDAP Group Search filter configuration is missing in your appsettings. Please ensure it's added under DirectorySearch:CustomSearchFilters:Group to enable group searches.");
return _dirSearchService.FindAllByUserCache(username: dirEntryUsername, filter, properties: propName).Then(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("User")]
public IActionResult GetUsersByGroupName(string? dirEntryUsername, [FromQuery] string? groupName = null)
{
try
{
string[] propName = { "memberof", "samaccountname", "givenname", "sn", "mail" };
dirEntryUsername ??= CurrentUser;
if (dirEntryUsername is null)
return Unauthorized();
_dirSearchService.CustomSearchFilters.TryGetValue("User", out string? filter);
if (filter is null)
throw new InvalidOperationException("The LDAP User Search filter configuration is missing in your appsettings. Please ensure it's added under DirectorySearch:CustomSearchFilters:User to enable group searches.");
return _dirSearchService.FindAllByUserCache(username: dirEntryUsername, filter, properties: propName).Then(
Success: data =>
{
if (groupName is not null)
data = data
.Where(rp => rp.PropertyNames.Cast<string>().Contains("memberof") &&
rp["memberof"].Cast<string>().Any(ldapDir => ldapDir.Contains(groupName)))
.ToList();
return Ok(data);
},
Fail: IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status424FailedDependency);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
private string? CurrentUser
{
get => (HttpContext.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value);
}
} }

View File

@@ -1,46 +1,44 @@
using DigitalData.UserManager.Application.Services; using DigitalData.UserManager.Application.Services;
using DigitalData.UserManager.Application.Services.Options; using DigitalData.UserManager.Application.Services.Options;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Route("api/[controller]")]
[ApiController]
public class EncryptionController : ControllerBase
{ {
[Route("api/[controller]")] private readonly Encryptor _encryptor;
[ApiController]
public class EncryptionController : ControllerBase public EncryptionController(Encryptor encryptor)
{ {
private readonly Encryptor _encryptor; _encryptor = encryptor;
}
public EncryptionController(Encryptor encryptor) [HttpPost("encrypt")]
{ public IActionResult Encrypt([FromQuery] string plainText, [FromBody] EncryptionParameters? options = null)
_encryptor = encryptor; {
} string cipherText = options is null
? _encryptor.Encrypt(plainText)
: Encryptor.Encrypt(plainText, options.Key, options.IV);
[HttpPost("encrypt")] return Ok(cipherText);
public IActionResult Encrypt([FromQuery] string plainText, [FromBody] EncryptionParameters? options = null) }
{
string cipherText = options is null
? _encryptor.Encrypt(plainText)
: Encryptor.Encrypt(plainText, options.Key, options.IV);
return Ok(cipherText); [HttpPost("decrypt")]
} public IActionResult Decrypt([FromQuery] string cipherText, [FromBody] EncryptionParameters? options = null)
{
var plainText = options is null
? _encryptor.Decrypt(cipherText)
: Encryptor.Decrypt(cipherText, options.Key, options.IV);
[HttpPost("decrypt")] return Ok(plainText);
public IActionResult Decrypt([FromQuery] string cipherText, [FromBody] EncryptionParameters? options = null) }
{
var plainText = options is null
? _encryptor.Decrypt(cipherText)
: Encryptor.Decrypt(cipherText, options.Key, options.IV);
return Ok(plainText); [HttpGet]
} public IActionResult Generate()
{
[HttpGet] var param = Encryptor.GenerateParameters();
public IActionResult Generate() return Ok(param);
{
var param = Encryptor.GenerateParameters();
return Ok(param);
}
} }
} }

View File

@@ -5,39 +5,38 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
{
[Authorize]
public class GroupController : BaseAuthController<IGroupService, GroupCreateDto, GroupReadDto, GroupUpdateDto, Group>
{
public GroupController(ILogger<GroupController> logger, IGroupService service, IUserService userService) : base(logger, service, userService)
{
}
[HttpPost("ByDir")] [Authorize]
public async Task<IActionResult> CreateByDir(DirectoryGroupDto adGroup) public class GroupController : BaseAuthController<IGroupService, GroupCreateDto, GroupReadDto, GroupUpdateDto, Group>
{
public GroupController(ILogger<GroupController> logger, IGroupService service, IUserService userService) : base(logger, service, userService)
{
}
[HttpPost("ByDir")]
public async Task<IActionResult> CreateByDir(DirectoryGroupDto adGroup)
{
try
{ {
try return await _service.CreateAsync(adGroup).ThenAsync(
{ Success: id =>
return await _service.CreateAsync(adGroup).ThenAsync( {
Success: id => var createdResource = new { Id = id };
{ var actionName = nameof(GetById);
var createdResource = new { Id = id }; var routeValues = new { id = createdResource.Id };
var actionName = nameof(GetById); return CreatedAtAction(actionName, routeValues, createdResource);
var routeValues = new { id = createdResource.Id }; },
return CreatedAtAction(actionName, routeValues, createdResource); Fail: IActionResult (m, n) =>
}, {
Fail: IActionResult (m, n) => _logger.LogNotice(n);
{ return BadRequest();
_logger.LogNotice(n); });
return BadRequest(); }
}); catch (Exception ex)
} {
catch (Exception ex) _logger.LogError(ex, "{Message}", ex.Message);
{ return StatusCode(StatusCodes.Status500InternalServerError);
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
} }
} }
} }

View File

@@ -5,77 +5,76 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Authorize]
public class GroupOfUserController : BaseAuthController<IGroupOfUserService, GroupOfUserCreateDto, GroupOfUserReadDto, GroupOfUserUpdateDto, GroupOfUser>
{ {
[Authorize] public GroupOfUserController(ILogger<GroupOfUserController> logger, IGroupOfUserService service, IUserService userService) : base(logger, service, userService)
public class GroupOfUserController : BaseAuthController<IGroupOfUserService, GroupOfUserCreateDto, GroupOfUserReadDto, GroupOfUserUpdateDto, GroupOfUser>
{ {
public GroupOfUserController(ILogger<GroupOfUserController> logger, IGroupOfUserService service, IUserService userService) : base(logger, service, userService) }
[HttpDelete]
public async Task<IActionResult> Delete([FromQuery] int groupId, [FromQuery] int userId)
{
try
{ {
} return await _service.DeleteAsyncByGroupUserId(groupId, userId).ThenAsync(Ok, IActionResult (m, n) =>
[HttpDelete]
public async Task<IActionResult> Delete([FromQuery] int groupId, [FromQuery] int userId)
{
try
{ {
return await _service.DeleteAsyncByGroupUserId(groupId, userId).ThenAsync(Ok, IActionResult (m, n) => _logger.LogNotice(n);
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
} });
} }
catch (Exception ex)
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery]bool withUser = false, [FromQuery]bool withGroup = false, [FromQuery] string? username = null)
{ {
try _logger.LogError(ex, "{Message}", ex.Message);
{ return StatusCode(StatusCodes.Status500InternalServerError);
if (username is not null) }
return await _service.ReadByUsernameAsync(username).ThenAsync(Ok, IActionResult (m, n) => }
{
_logger.LogNotice(n);
return NotFound();
});
return await _service.ReadAllAsyncWith(withUser, withGroup).ThenAsync(Ok, IActionResult (m, n) => [NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery]bool withUser = false, [FromQuery]bool withGroup = false, [FromQuery] string? username = null)
{
try
{
if (username is not null)
return await _service.ReadByUsernameAsync(username).ThenAsync(Ok, IActionResult (m, n) =>
{ {
_logger.LogNotice(n); _logger.LogNotice(n);
return NotFound(); return NotFound();
}); });
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Has")] return await _service.ReadAllAsyncWith(withUser, withGroup).ThenAsync(Ok, IActionResult (m, n) =>
public async Task<IActionResult> HasGroup([FromQuery] string username, [FromQuery] string groupname) {
_logger.LogNotice(n);
return NotFound();
});
}
catch(Exception ex)
{ {
try _logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("Has")]
public async Task<IActionResult> HasGroup([FromQuery] string username, [FromQuery] string groupname)
{
try
{
return await _service.HasGroup(username, groupname).ThenAsync(Ok, (m, n) =>
{ {
return await _service.HasGroup(username, groupname).ThenAsync(Ok, (m, n) => _logger.LogNotice(n);
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
} });
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
} }
} }
} }

View File

@@ -4,13 +4,12 @@ using DigitalData.UserManager.Application.DTOs.Module;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Authorize]
public class ModuleController : ReadControllerBaseWithErrorHandling<IModuleService, ModuleDto, Module, int>
{ {
[Authorize] public ModuleController(ILogger<ModuleController> logger, IModuleService service) : base(logger, service)
public class ModuleController : ReadControllerBaseWithErrorHandling<IModuleService, ModuleDto, Module, int>
{ {
public ModuleController(ILogger<ModuleController> logger, IModuleService service) : base(logger, service)
{
}
} }
} }

View File

@@ -6,48 +6,47 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Authorize]
public class ModuleOfUserController : CRUDControllerBaseWithErrorHandling<IModuleOfUserService, ModuleOfUserCreateDto, ModuleOfUserReadDto, ModuleOfUserUpdateDto, ModuleOfUser, int>
{ {
[Authorize] public ModuleOfUserController(ILogger<ModuleOfUserController> logger, IModuleOfUserService service) : base(logger, service)
public class ModuleOfUserController : CRUDControllerBaseWithErrorHandling<IModuleOfUserService, ModuleOfUserCreateDto, ModuleOfUserReadDto, ModuleOfUserUpdateDto, ModuleOfUser, int>
{ {
public ModuleOfUserController(ILogger<ModuleOfUserController> logger, IModuleOfUserService service) : base(logger, service) }
{
}
[NonAction] [NonAction]
public override Task<IActionResult> GetAll() => base.GetAll(); public override Task<IActionResult> GetAll() => base.GetAll();
[HttpGet] [HttpGet]
public async Task<IActionResult> GetAll(string? username = null) public async Task<IActionResult> GetAll(string? username = null)
{ {
if (username is not null) if (username is not null)
return await _service.ReadByUserAsync(username).ThenAsync(Ok, IActionResult (m, n) => return await _service.ReadByUserAsync(username).ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
return await base.GetAll();
}
[HttpDelete]
public async Task<IActionResult> Delete([FromQuery] int moduleId, [FromQuery]int userId)
{
try
{ {
return await _service.DeleteAsyncByModuleUserId(moduleId, userId).ThenAsync(Ok, IActionResult (m, n) => _logger.LogNotice(n);
{
_logger.LogNotice(n);
return BadRequest();
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError); return StatusCode(StatusCodes.Status500InternalServerError);
} });
return await base.GetAll();
}
[HttpDelete]
public async Task<IActionResult> Delete([FromQuery] int moduleId, [FromQuery]int userId)
{
try
{
return await _service.DeleteAsyncByModuleUserId(moduleId, userId).ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return BadRequest();
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
} }
} }
} }

View File

@@ -1,4 +1,3 @@
using DigitalData.Core.API;
using DigitalData.Core.DTO; using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
@@ -6,95 +5,94 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
[Authorize]
public class UserController : BaseAuthController<IUserService, UserCreateDto, UserReadDto, UserUpdateDto, User>
{ {
[Authorize] public UserController(ILogger<UserController> logger, IUserService service) : base(logger, service, service)
public class UserController : BaseAuthController<IUserService, UserCreateDto, UserReadDto, UserUpdateDto, User>
{ {
public UserController(ILogger<UserController> logger, IUserService service) : base(logger, service, service) }
{
}
[HttpGet("ByModuleId/{moduleId}")] [HttpGet("ByModuleId/{moduleId}")]
public async Task<IActionResult> GetByModuleId([FromRoute] int moduleId, [FromQuery]bool assigned = true) public async Task<IActionResult> GetByModuleId([FromRoute] int moduleId, [FromQuery]bool assigned = true)
{
try
{ {
try return await (assigned ? _service.ReadByModuleIdAsync(moduleId) : _service.ReadUnassignedByModuleIdAsync(moduleId))
{ .ThenAsync(Ok, IActionResult(m, n) =>
return await (assigned ? _service.ReadByModuleIdAsync(moduleId) : _service.ReadUnassignedByModuleIdAsync(moduleId))
.ThenAsync(Ok, IActionResult(m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("ByGroupId/{groupId}")]
public async Task<IActionResult> GetByGroupId([FromRoute] int groupId, [FromQuery] bool assigned = true)
{
try
{
return await (assigned ? _service.ReadByGroupIdAsync(groupId) : _service.ReadUnassignedByGroupIdAsync(groupId))
.ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("ByDir")]
public async Task<IActionResult> CreateByDir(UserPrincipalDto upDto)
{
try
{
return await _service.CreateAsync(upDto).ThenAsync(
Success: id =>
{
var createdResource = new { Id = id };
var actionName = nameof(GetById);
var routeValues = new { id = createdResource.Id };
return CreatedAtAction(actionName, routeValues, createdResource);
},
Fail: IActionResult (m, n) =>
{
_logger.LogNotice(n);
return BadRequest();
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("ByUsername/{username}")]
public virtual async Task<IActionResult> GetByUsername([FromRoute] string username)
{
try
{
return await _service.ReadByUsernameAsync(username).ThenAsync(Ok, IActionResult (m, n) =>
{ {
_logger.LogNotice(n); _logger.LogNotice(n);
return NotFound(); return StatusCode(StatusCodes.Status500InternalServerError);
}); });
} }
catch(Exception ex) catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("ByGroupId/{groupId}")]
public async Task<IActionResult> GetByGroupId([FromRoute] int groupId, [FromQuery] bool assigned = true)
{
try
{
return await (assigned ? _service.ReadByGroupIdAsync(groupId) : _service.ReadUnassignedByGroupIdAsync(groupId))
.ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpPost("ByDir")]
public async Task<IActionResult> CreateByDir(UserPrincipalDto upDto)
{
try
{
return await _service.CreateAsync(upDto).ThenAsync(
Success: id =>
{
var createdResource = new { Id = id };
var actionName = nameof(GetById);
var routeValues = new { id = createdResource.Id };
return CreatedAtAction(actionName, routeValues, createdResource);
},
Fail: IActionResult (m, n) =>
{
_logger.LogNotice(n);
return BadRequest();
});
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[HttpGet("ByUsername/{username}")]
public virtual async Task<IActionResult> GetByUsername([FromRoute] string username)
{
try
{
return await _service.ReadByUsernameAsync(username).ThenAsync(Ok, IActionResult (m, n) =>
{ {
_logger.LogError(ex, "{Message}", ex.Message); _logger.LogNotice(n);
return StatusCode(StatusCodes.Status500InternalServerError); return NotFound();
} });
}
catch(Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
} }
} }
} }

View File

@@ -5,38 +5,37 @@ using DigitalData.UserManager.Domain.Entities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace DigitalData.UserManager.API.Controllers namespace DigitalData.UserManager.API.Controllers;
{
[Authorize]
public class UserRepController : BaseAuthController<IUserRepService, UserRepCreateDto, UserRepReadDto, UserRepUpdateDto, UserRep>
{
public UserRepController(ILogger<UserRepController> logger, IUserRepService service, IUserService userService) : base(logger, service, userService)
{
}
[NonAction] [Authorize]
public override Task<IActionResult> GetAll() public class UserRepController : BaseAuthController<IUserRepService, UserRepCreateDto, UserRepReadDto, UserRepUpdateDto, UserRep>
{
public UserRepController(ILogger<UserRepController> logger, IUserRepService service, IUserService userService) : base(logger, service, userService)
{
}
[NonAction]
public override Task<IActionResult> GetAll()
{
return base.GetAll();
}
[HttpGet]
public async Task<IActionResult> GetAll(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null)
{
try
{ {
return base.GetAll(); return await _service.ReadAllAsync(withUser: withUser, withRepGroup: withRepGroup, withGroup: withGroup, withRepUser: withRepUser,
userId: userId, groupId: groupId).ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return NotFound();
});
} }
catch (Exception ex)
[HttpGet]
public async Task<IActionResult> GetAll(bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, int? userId = null, int? groupId = null)
{ {
try _logger.LogError(ex, "{Message}", ex.Message);
{ return StatusCode(StatusCodes.Status500InternalServerError);
return await _service.ReadAllAsync(withUser: withUser, withRepGroup: withRepGroup, withGroup: withGroup, withRepUser: withRepUser,
userId: userId, groupId: groupId).ThenAsync(Ok, IActionResult (m, n) =>
{
_logger.LogNotice(n);
return NotFound();
});
}
catch (Exception ex)
{
_logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
} }
} }
} }

View File

@@ -1,12 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Version>4.0.0.0</Version> <Version>6.0.1</Version>
<AssemblyVersion>4.0.0.0</AssemblyVersion> <AssemblyVersion>6.0.1</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion> <FileVersion>6.0.1</FileVersion>
<PackageIcon>icon.png</PackageIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -20,29 +21,20 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.API" Version="2.0.0" /> <PackageReference Include="DigitalData.Auth.Client" Version="1.3.5" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.14" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.20" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.20" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" /> <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NLog" Version="5.3.2" /> <PackageReference Include="NLog" Version="5.3.2" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.11" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.DirectoryServices" Version="7.0.1" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DigitalData.UserManager.Application\DigitalData.UserManager.Application.csproj" />
<ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -50,6 +42,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\Assets\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="wwwroot\assets\img\DD_white.svg" /> <None Include="wwwroot\assets\img\DD_white.svg" />
<None Include="wwwroot\assets\img\digital_data.svg" /> <None Include="wwwroot\assets\img\digital_data.svg" />
<None Include="wwwroot\assets\img\digital_data_red_BG.svg" /> <None Include="wwwroot\assets\img\digital_data_red_BG.svg" />
@@ -63,4 +59,8 @@
<None Include="wwwroot\media\bootstrap-icons-X6UQXWUS.woff2" /> <None Include="wwwroot\media\bootstrap-icons-X6UQXWUS.woff2" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DigitalData.UserManager.DependencyInjection\DigitalData.UserManager.DependencyInjection.csproj" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,18 @@
namespace DigitalData.UserManager.API;
public class LazyServiceProvider : IServiceProvider
{
private Lazy<IServiceProvider>? _serviceProvider;
public Func<IServiceProvider> Factory
{
set => _serviceProvider = new(value);
}
public object? GetService(Type serviceType)
{
if (_serviceProvider is null)
throw new InvalidOperationException("GetService cannot be called before _serviceProvider is set.");
return _serviceProvider.Value.GetService(serviceType);
}
}

View File

@@ -0,0 +1,12 @@
namespace DigitalData.UserManager.API.Models;
public class AuthTokenKeys
{
public string Cookie { get; init; } = "AuthToken";
public string QueryString { get; init; } = "AuthToken";
public string Issuer { get; init; } = "auth.digitaldata.works";
public string Audience { get; init; } = "user-manager.digitaldata.works";
}

View File

@@ -0,0 +1,18 @@
using DigitalData.UserManager.Application.DTOs.User;
using System.Security.Claims;
namespace DigitalData.UserManager.API.Models;
public static class ModelExtensions
{
public static List<Claim> ToClaimList(this UserReadDto user) => new()
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? "")
};
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object)claim.Value);
}

View File

@@ -10,20 +10,27 @@ using DigitalData.UserManager.API.Controllers;
using DigitalData.UserManager.Application.Services; using DigitalData.UserManager.Application.Services;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Newtonsoft.Json; using Newtonsoft.Json;
using Microsoft.IdentityModel.Tokens;
using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.API.Models;
using DigitalData.Auth.Client;
using DigitalData.UserManager.API;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using DigitalData.Core.Abstractions.Security.Extensions;
using Microsoft.OpenApi.Models;
using DigitalData.UserManager.DependencyInjection;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger(); var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Debug("init main"); logger.Debug("init main");
try { try {
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder();
var config = builder.Configuration; var config = builder.Configuration;
builder.Services.AddEncryptor(builder.Configuration.GetSection("EncryptionParameters")); builder.Services.AddEncryptor(builder.Configuration.GetSection("EncryptionParameters"));
if (builder.Configuration.GetValue<bool>("RunAsWindowsService"))
builder.Host.UseWindowsService();
builder.Logging.ClearProviders(); builder.Logging.ClearProviders();
builder.Host.UseNLog(); builder.Host.UseNLog();
@@ -63,7 +70,7 @@ try {
// Once the app is built, the password will be decrypted with Encryptor. lazy loading also acts as a call back method. // Once the app is built, the password will be decrypted with Encryptor. lazy loading also acts as a call back method.
Lazy<string>? cnn_str = null; Lazy<string>? cnn_str = null;
builder.Services.AddDbContext<UserManagerDbContext>(options => options.UseSqlServer(cnn_str!.Value).EnableSensitiveDataLogging()); builder.Services.AddUserManager(options => options.UseSqlServer(cnn_str!.Value).EnableSensitiveDataLogging());
var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>() ?? throw new InvalidOperationException("In appsettings there is no allowed origin."); var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>() ?? throw new InvalidOperationException("In appsettings there is no allowed origin.");
@@ -78,17 +85,93 @@ try {
.AllowCredentials(); .AllowCredentials();
}); });
}); });
//builder.Services.AddAutoMapper(typeof(DirectoryMappingProfile).Assembly);
builder.Services.AddUserManager<UserManagerDbContext>();
builder.ConfigureBySection<DirectorySearchOptions>(); builder.ConfigureBySection<DirectorySearchOptions>();
builder.Services.AddDirectorySearchService(); builder.Services.AddDirectorySearchService(config.GetSection("DirectorySearchOptions"));
builder.Services.AddJWTService<UserReadDto>(user => new SecurityTokenDescriptor()
{
Claims = user.ToClaimList().ToDictionary(claim => claim.Type, claim => claim.Value as object)
});
var lazyProvider = new LazyServiceProvider();
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get<AuthTokenKeys>() ?? new();
builder.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
{
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
return new List<SecurityKey>() { publicKey.SecurityKey };
},
ValidateIssuer = true,
ValidIssuer = authTokenKeys.Issuer,
ValidateAudience = true,
ValidAudience = authTokenKeys.Audience,
};
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// if there is no token read related cookie or query string
if (context.Token is null) // if there is no token
{
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
context.Token = cookieToken;
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
context.Token = queryStrToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddSwaggerGen(setupAct =>
{
setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
builder.Services.AddCookieBasedLocalizer(); builder.Services.AddCookieBasedLocalizer();
var app = builder.Build(); var app = builder.Build();
lazyProvider.Factory = () => app.Services;
cnn_str = new(() => cnn_str = new(() =>
{ {
var encryptor = app.Services.GetRequiredService<Encryptor>(); var encryptor = app.Services.GetRequiredService<Encryptor>();

View File

@@ -11,9 +11,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish> <LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data> <ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid> <ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\DD-UserManager\Web\${Version}\$(Version).zip</DesktopBuildPackageLocation> <DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\DD-UserManager\Web\$(Version)\$(Version).zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile> <PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>UserManager.API</DeployIisAppPath> <DeployIisAppPath>UserManager.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId> <_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>07ccd651-647c-49f7-9715-30cebc13710d</ProjectGuid>
<DesktopBuildPackageLocation>E:\TekH\Visual Studio\src\DigitalData.UserManager.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>UserManager</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
</PropertyGroup>
</Project>

View File

@@ -22,7 +22,7 @@
"https": { "https": {
"commandName": "Project", "commandName": "Project",
"dotnetRunMessages": true, "dotnetRunMessages": true,
"launchBrowser": true, "launchBrowser": false,
"launchUrl": "swagger", "launchUrl": "swagger",
"applicationUrl": "https://localhost:7103;http://localhost:5137", "applicationUrl": "https://localhost:7103;http://localhost:5137",
"environmentVariables": { "environmentVariables": {

View File

@@ -75,5 +75,16 @@
"DateTimeZoneHandling": "Local", "DateTimeZoneHandling": "Local",
// Delete below in production // Delete below in production
"UseEncryptor": true, "UseEncryptor": true,
"UseSwagger": true "UseSwagger": true,
"AuthClientParams": {
"Url": "https://localhost:7192/auth-hub",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "user-manager.digitaldata.works",
"Content": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QCd7dH/xOUITFZbitMa/xnh8a0LyL6ZBvSRAwkI9ceplTRSHJXoM1oB+xtjWE1kOuHVLe941Tm03szS4+/rHIm0Ejva/KKlv7sPFAHE/pWuoPS303vOHgI4HAFcuwywA8CghUWzaaK5LU/Hl8srWwxBHv5hKIUjJFJygeAIENvFOZ1gFbB3MPEC99PiPOwAmfl4tMQUmSsFyspl/RWVi7bTv26ZE+m3KPcWppmvmYjXlSitxRaySxnfFvpca/qWfd/uUUg2KWKtpAwWVkqr0qD9v3TyKSgHoGDsrFpwSx8qufUJSinmZ1u/0iKl6TXeHubYS4C4SUSVjOWXymI2ZQIDAQAB-----END PUBLIC KEY-----"
}
],
"RetryDelay": "00:00:05"
}
} }

View File

@@ -0,0 +1,9 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IClientUserRepository : ICRUDRepository<ClientUser, int>
{
Task<IEnumerable<ClientUser>> ReadAsync(bool readOnly = true, int? userId = null);
}

View File

@@ -0,0 +1,27 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IGroupOfUserRepository : ICRUDRepository<GroupOfUser, int>
{
IQueryable<GroupOfUser> ReadByGroupId(int groupId);
Task<IEnumerable<GroupOfUser>> ReadByGroupUserIdAsync(int groupId, int userId);
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithGroup();
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithUser();
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithGroupAndUser();
Task<IEnumerable<GroupOfUser>> ReadByUsernameAsync(string username);
Task<IEnumerable<GroupOfUser>> ReadByUserIdAsync(int userId);
// merge all GroupOfUserRepository-methods conditionally under this method
Task<IEnumerable<GroupOfUser>> ReadAsync(
bool readOnly = true,
bool withGroup = true, bool withUser = true,
int? id = null, int? groupId = null, int? userId = null, string? username = null);
}

View File

@@ -0,0 +1,8 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IGroupRepository : ICRUDRepository<Group, int>
{
}

View File

@@ -0,0 +1,13 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IModuleOfUserRepository : ICRUDRepository<ModuleOfUser, int>
{
IQueryable<ModuleOfUser> ReadByModuleId(int moduleId);
Task<IEnumerable<ModuleOfUser>> ReadByModelUserIdAsync(int moduleId, int userId);
Task<IEnumerable<ModuleOfUser>> ReadByUserAsync(string username);
}

View File

@@ -0,0 +1,8 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IModuleRepository : ICRUDRepository<Module, int>
{
}

View File

@@ -0,0 +1,11 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IUserRepRepository : ICRUDRepository<UserRep, int>
{
Task<IEnumerable<UserRep>> ReadAllAsync(
bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false,
int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true);
}

View File

@@ -0,0 +1,17 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Application.Contracts.Repositories;
public interface IUserRepository : ICRUDRepository<User, int>
{
Task<IEnumerable<User>> ReadByModuleIdAsync(int moduleId);
Task<IEnumerable<User>> ReadUnassignedByModuleIdAsync(int moduleId);
Task<IEnumerable<User>> ReadByGroupIdAsync(int groupId);
Task<IEnumerable<User>> ReadUnassignedByGroupIdAsync(int groupId);
Task<User?> ReadByUsernameAsync(string username);
}

View File

@@ -2,9 +2,6 @@
using DigitalData.UserManager.Application.MappingProfiles; using DigitalData.UserManager.Application.MappingProfiles;
using DigitalData.UserManager.Application.Services; using DigitalData.UserManager.Application.Services;
using DigitalData.UserManager.Application.Services.Options; using DigitalData.UserManager.Application.Services.Options;
using DigitalData.UserManager.Infrastructure.Contracts;
using DigitalData.UserManager.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -19,8 +16,7 @@ namespace DigitalData.UserManager.Application
/// <typeparam name="TDbContext">The type of the DbContext to use for the repositories.</typeparam> /// <typeparam name="TDbContext">The type of the DbContext to use for the repositories.</typeparam>
/// <param name="services">The IServiceCollection to which the services will be added.</param> /// <param name="services">The IServiceCollection to which the services will be added.</param>
/// <returns>The updated IServiceCollection.</returns> /// <returns>The updated IServiceCollection.</returns>
public static IServiceCollection AddUserManager<TDbContext>(this IServiceCollection services) public static IServiceCollection AddUserManagerApplication(this IServiceCollection services)
where TDbContext : DbContext, IUserManagerDbContext
=> services => services
.AddAutoMapper(typeof(UserMappingProfile).Assembly) .AddAutoMapper(typeof(UserMappingProfile).Assembly)
.AddAutoMapper(typeof(GroupMappingProfile).Assembly) .AddAutoMapper(typeof(GroupMappingProfile).Assembly)
@@ -29,31 +25,13 @@ namespace DigitalData.UserManager.Application
.AddAutoMapper(typeof(ModuleOfUserMappingProfile).Assembly) .AddAutoMapper(typeof(ModuleOfUserMappingProfile).Assembly)
.AddAutoMapper(typeof(UserRepMappingProfile).Assembly) .AddAutoMapper(typeof(UserRepMappingProfile).Assembly)
.AddScoped<IUserRepository, UserRepository<TDbContext>>()
.AddScoped<IGroupRepository, GroupRepository<TDbContext>>()
.AddScoped<IGroupOfUserRepository, GroupOfUserRepository<TDbContext>>()
.AddScoped<IModuleRepository, ModuleRepository<TDbContext>>()
.AddScoped<IModuleOfUserRepository, ModuleOfUserRepository<TDbContext>>()
.AddScoped<IUserRepRepository, UserRepRepository<TDbContext>>()
.AddScoped<IClientUserRepository, ClientUserRepository<TDbContext>>()
.AddScoped<IUserService, UserService>() .AddScoped<IUserService, UserService>()
.AddScoped<IGroupService, GroupService>() .AddScoped<IGroupService, GroupService>()
.AddScoped<IGroupOfUserService, GroupOfUserService>() .AddScoped<IGroupOfUserService, GroupOfUserService>()
.AddScoped<IModuleService, ModuleService>() .AddScoped<IModuleService, ModuleService>()
.AddScoped<IModuleOfUserService, ModuleOfUserService>() .AddScoped<IModuleOfUserService, ModuleOfUserService>()
.AddScoped<IUserRepService, UserRepService>(); .AddScoped<IUserRepService, UserRepService>();
/// <summary>
/// Adds the UserManager services and repositories to the specified <see cref="IServiceCollection"/>.
/// This method registers the necessary mappings, repositories, services and <see cref="UserManagerDbContext"/> for the UserManager.
/// </summary>
/// <param name="services">The IServiceCollection to which the services will be added.</param>
/// <returns>The updated IServiceCollection.</returns>
public static IServiceCollection AddUserManager(this IServiceCollection services, string connectionString)=> services
.AddDbContext<UserManagerDbContext>(options => options.UseSqlServer(connectionString).EnableSensitiveDataLogging())
.AddUserManager<UserManagerDbContext>();
public static IServiceCollection AddEncryptor(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddEncryptor(this IServiceCollection services, IConfiguration configuration)
{ {
services.AddSingleton<Encryptor>(); services.AddSingleton<Encryptor>();

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PackageId>UserManager.Application</PackageId> <PackageId>UserManager.Application</PackageId>
<Version>3.1.2</Version> <Version>3.1.3</Version>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>UserManager.Application</Product> <Product>UserManager.Application</Product>
@@ -14,23 +14,26 @@
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl>
<PackageTags>digital data application user maanger</PackageTags> <PackageTags>digital data application user maanger</PackageTags>
<AssemblyVersion>3.1.2</AssemblyVersion> <AssemblyVersion>3.1.3</AssemblyVersion>
<FileVersion>3.1.2</FileVersion> <FileVersion>3.1.3</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\DigitalData.UserManager.Domain\Assets\icon.png"> <None Include="..\Assets\icon.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="3.4.1" />
<PackageReference Include="DigitalData.Core.Abstractions" Version="3.1.0" /> <PackageReference Include="DigitalData.Core.Application" Version="3.2.1" />
<PackageReference Include="DigitalData.Core.Application" Version="3.0.1" />
<PackageReference Include="DigitalData.Core.DTO" Version="2.0.1" /> <PackageReference Include="DigitalData.Core.DTO" Version="2.0.1" />
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="2.0.0" /> <PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="3.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" /> <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
<PackageReference Include="System.DirectoryServices" Version="7.0.1" /> <PackageReference Include="System.DirectoryServices" Version="7.0.1" />
@@ -38,16 +41,26 @@
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" /> <PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" /> <PackageReference Include="AutoMapper" Version="14.0.0" />
<ProjectReference Include="..\DigitalData.UserManager.Infrastructure\DigitalData.UserManager.Infrastructure.csproj" /> <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
<PackageReference Include="System.DirectoryServices" Version="8.0.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.1" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.4" />
<PackageReference Include="System.DirectoryServices" Version="9.0.4" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="9.0.4" />
<PackageReference Include="System.DirectoryServices.Protocols" Version="9.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Assets\icon.png"> <ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" />
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -3,7 +3,7 @@ using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.GroupOfUser; using DigitalData.UserManager.Application.DTOs.GroupOfUser;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
namespace DigitalData.UserManager.Application.Services namespace DigitalData.UserManager.Application.Services
{ {

View File

@@ -3,7 +3,7 @@ using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.Group; using DigitalData.UserManager.Application.DTOs.Group;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
namespace DigitalData.UserManager.Application.Services namespace DigitalData.UserManager.Application.Services

View File

@@ -4,7 +4,7 @@ using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.ModuleOfUser; using DigitalData.UserManager.Application.DTOs.ModuleOfUser;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
namespace DigitalData.UserManager.Application.Services namespace DigitalData.UserManager.Application.Services
{ {

View File

@@ -3,7 +3,7 @@ using DigitalData.Core.Application;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.Module; using DigitalData.UserManager.Application.DTOs.Module;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
namespace DigitalData.UserManager.Application.Services namespace DigitalData.UserManager.Application.Services
{ {

View File

@@ -3,7 +3,7 @@ using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.UserRep; using DigitalData.UserManager.Application.DTOs.UserRep;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@@ -3,7 +3,7 @@ using DigitalData.Core.DTO;
using DigitalData.UserManager.Application.Contracts; using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User; using DigitalData.UserManager.Application.DTOs.User;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
namespace DigitalData.UserManager.Application.Services namespace DigitalData.UserManager.Application.Services

View File

@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>UserManager</PackageId>
<Version>1.0.0</Version>
<Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company>
<Product>UserManager</Product>
<Description>Die Hauptbibliothek, die Benutzer-, Gruppen- und Modulzuweisungen über Active Directory mit Entity Framework durchführt.</Description>
<Copyright>Copyright 2024</Copyright>
<PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl>
<PackageTags>digital data user maanger</PackageTags>
<AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<None Include="..\Assets\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DigitalData.UserManager.Application\DigitalData.UserManager.Application.csproj" />
<ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" />
<ProjectReference Include="..\DigitalData.UserManager.Infrastructure\DigitalData.UserManager.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,25 @@
using DigitalData.UserManager.Application;
using DigitalData.UserManager.Infrastructure;
using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace DigitalData.UserManager.DependencyInjection;
public static class Extensions
{
public static IServiceCollection AddUserManager<TDbContext>(this IServiceCollection services)
where TDbContext : DbContext, IUserManagerDbContext
{
services.AddUserManagerInfrastructure<TDbContext>();
services.AddUserManagerApplication();
return services;
}
public static IServiceCollection AddUserManager(this IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction)
{
services.AddUserManagerInfrastructure(optionsAction);
services.AddUserManagerApplication();
return services;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PackageId>UserManager.Domain</PackageId> <PackageId>UserManager.Domain</PackageId>
<Version>3.0.1</Version> <Version>3.0.2</Version>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>UserManager.Domain</Product> <Product>UserManager.Domain</Product>
@@ -14,26 +14,19 @@
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl>
<PackageTags>digital data domain user maanger</PackageTags> <PackageTags>digital data domain user maanger</PackageTags>
<AssemblyVersion>3.0.1</AssemblyVersion> <AssemblyVersion>3.0.2</AssemblyVersion>
<FileVersion>3.0.1</FileVersion> <FileVersion>3.0.2</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\DigitalData.UserManager.Application\Assets\icon.png"> <None Include="..\Assets\icon.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Abstractions" Version="3.1.0" /> <PackageReference Include="DigitalData.Core.Abstractions" Version="3.4.0" />
</ItemGroup>
<ItemGroup>
<None Update="Assets\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -1,10 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IClientUserRepository : ICRUDRepository<ClientUser, int>
{
Task<IEnumerable<ClientUser>> ReadAsync(bool readOnly = true, int? userId = null);
}
}

View File

@@ -1,28 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IGroupOfUserRepository : ICRUDRepository<GroupOfUser, int>
{
IQueryable<GroupOfUser> ReadByGroupId(int groupId);
Task<IEnumerable<GroupOfUser>> ReadByGroupUserIdAsync(int groupId, int userId);
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithGroup();
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithUser();
Task<IEnumerable<GroupOfUser>> ReadAllAsyncWithGroupAndUser();
Task<IEnumerable<GroupOfUser>> ReadByUsernameAsync(string username);
Task<IEnumerable<GroupOfUser>> ReadByUserIdAsync(int userId);
// merge all GroupOfUserRepository-methods conditionally under this method
Task<IEnumerable<GroupOfUser>> ReadAsync(
bool readOnly = true,
bool withGroup = true, bool withUser = true,
int? id = null, int? groupId = null, int? userId = null, string? username = null);
}
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IGroupRepository : ICRUDRepository<Group, int>
{
}
}

View File

@@ -1,14 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IModuleOfUserRepository : ICRUDRepository<ModuleOfUser, int>
{
IQueryable<ModuleOfUser> ReadByModuleId(int moduleId);
Task<IEnumerable<ModuleOfUser>> ReadByModelUserIdAsync(int moduleId, int userId);
Task<IEnumerable<ModuleOfUser>> ReadByUserAsync(string username);
}
}

View File

@@ -1,9 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IModuleRepository : ICRUDRepository<Module, int>
{
}
}

View File

@@ -1,22 +1,21 @@
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Contracts namespace DigitalData.UserManager.Infrastructure.Contracts;
public interface IUserManagerDbContext
{ {
public interface IUserManagerDbContext public DbSet<GroupOfUser> GroupOfUsers { get; }
{
public DbSet<GroupOfUser> GroupOfUsers { get; }
public DbSet<Group> Groups { get; } public DbSet<Group> Groups { get; }
public DbSet<ModuleOfUser> ModuleOfUsers { get; } public DbSet<ModuleOfUser> ModuleOfUsers { get; }
public DbSet<Module> Modules { get; } public DbSet<Module> Modules { get; }
public DbSet<User> Users { get; } public DbSet<User> Users { get; }
public DbSet<UserRep> UserReps { get; } public DbSet<UserRep> UserReps { get; }
public DbSet<ClientUser> ClientUsers { get; } public DbSet<ClientUser> ClientUsers { get; }
}
} }

View File

@@ -1,12 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IUserRepRepository : ICRUDRepository<UserRep, int>
{
Task<IEnumerable<UserRep>> ReadAllAsync(
bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false,
int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true);
}
}

View File

@@ -1,18 +0,0 @@
using DigitalData.Core.Abstractions.Infrastructure;
using DigitalData.UserManager.Domain.Entities;
namespace DigitalData.UserManager.Infrastructure.Contracts
{
public interface IUserRepository : ICRUDRepository<User, int>
{
Task<IEnumerable<User>> ReadByModuleIdAsync(int moduleId);
Task<IEnumerable<User>> ReadUnassignedByModuleIdAsync(int moduleId);
Task<IEnumerable<User>> ReadByGroupIdAsync(int groupId);
Task<IEnumerable<User>> ReadUnassignedByGroupIdAsync(int groupId);
Task<User?> ReadByUsernameAsync(string username);
}
}

View File

@@ -0,0 +1,42 @@
using DigitalData.UserManager.Application;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Infrastructure.Contracts;
using DigitalData.UserManager.Infrastructure.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace DigitalData.UserManager.Infrastructure;
public static class DependencyInjection
{
/// <summary>
/// Adds the UserManager repositories to the specified <see cref="IServiceCollection"/>.
/// This method registers the necessary repositories for the UserManager.
/// </summary>
/// <typeparam name="TDbContext">The type of the DbContext to use for the repositories.</typeparam>
/// <param name="services">The IServiceCollection to which the services will be added.</param>
/// <returns>The updated IServiceCollection.</returns>
public static IServiceCollection AddUserManagerInfrastructure<TDbContext>(this IServiceCollection services)
where TDbContext : DbContext, IUserManagerDbContext
{
return services
.AddScoped<IUserRepository, UserRepository<TDbContext>>()
.AddScoped<IGroupRepository, GroupRepository<TDbContext>>()
.AddScoped<IGroupOfUserRepository, GroupOfUserRepository<TDbContext>>()
.AddScoped<IModuleRepository, ModuleRepository<TDbContext>>()
.AddScoped<IModuleOfUserRepository, ModuleOfUserRepository<TDbContext>>()
.AddScoped<IUserRepRepository, UserRepRepository<TDbContext>>()
.AddScoped<IClientUserRepository, ClientUserRepository<TDbContext>>();
}
/// <summary>
/// Adds the UserManager services and repositories to the specified <see cref="IServiceCollection"/>.
/// This method allows configuring the DbContext options using the provided <paramref name="optionsAction"/>.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to which the services will be added.</param>
/// <param name="optionsAction">An <see cref="Action{T}"/> to configure the <see cref="DbContextOptionsBuilder"/>.</param>
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddUserManagerInfrastructure(this IServiceCollection services, Action<DbContextOptionsBuilder> optionsAction) => services
.AddDbContext<UserManagerDbContext>(optionsAction)
.AddUserManagerInfrastructure<UserManagerDbContext>();
}

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<PackageId>UserManager.Infrastructure</PackageId> <PackageId>UserManager.Infrastructure</PackageId>
<Version>3.0.1</Version> <Version>3.0.2</Version>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>UserManager.Infrastructure</Product> <Product>UserManager.Infrastructure</Product>
@@ -14,36 +14,39 @@
<PackageIcon>icon.png</PackageIcon> <PackageIcon>icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebUserManager.git</RepositoryUrl>
<PackageTags>digital data infrastructure user maanger</PackageTags> <PackageTags>digital data infrastructure user maanger</PackageTags>
<AssemblyVersion>3.0.1</AssemblyVersion> <AssemblyVersion>3.0.2</AssemblyVersion>
<FileVersion>3.0.1</FileVersion> <FileVersion>3.0.2</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\DigitalData.UserManager.Domain\Assets\icon.png"> <None Include="..\Assets\icon.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.0" /> <PackageReference Include="DigitalData.Core.Infrastructure" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.16" /> </ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PrivateAssets>all</PrivateAssets> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
</PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" /> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.15" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\DigitalData.UserManager.Application\DigitalData.UserManager.Application.csproj" />
<ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" /> <ProjectReference Include="..\DigitalData.UserManager.Domain\DigitalData.UserManager.Domain.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="Assets\icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
</Project> </Project>

View File

@@ -1,7 +1,8 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Application.Contracts.Repositories;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DigitalData.UserManager.Infrastructure.Contracts;
namespace DigitalData.UserManager.Infrastructure.Repositories namespace DigitalData.UserManager.Infrastructure.Repositories
{ {

View File

@@ -2,6 +2,7 @@
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using DigitalData.UserManager.Application.Contracts.Repositories;
namespace DigitalData.UserManager.Infrastructure.Repositories namespace DigitalData.UserManager.Infrastructure.Repositories
{ {

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;

View File

@@ -1,32 +1,32 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Repositories namespace DigitalData.UserManager.Infrastructure.Repositories;
public class ModuleOfUserRepository<TDbContext> : CRUDRepository<ModuleOfUser, int, TDbContext>, IModuleOfUserRepository
where TDbContext : DbContext, IUserManagerDbContext
{ {
public class ModuleOfUserRepository<TDbContext> : CRUDRepository<ModuleOfUser, int, TDbContext>, IModuleOfUserRepository public ModuleOfUserRepository(TDbContext dbContext) : base(dbContext, dbContext.ModuleOfUsers)
where TDbContext : DbContext, IUserManagerDbContext
{ {
public ModuleOfUserRepository(TDbContext dbContext) : base(dbContext, dbContext.ModuleOfUsers)
{
}
private IQueryable<ModuleOfUser> ReadByUser(string username)
{
return _dbSet.Where(mou => mou.User!.Username == username).Include(mou => mou.Module);
}
public IQueryable<ModuleOfUser> ReadByModuleId(int moduleId)
{
return _dbSet.Where(mou => mou.ModuleId == moduleId);
}
public async Task<IEnumerable<ModuleOfUser>> ReadByModelUserIdAsync(int moduleId, int userId)
{
return await _dbSet.Where(mou => mou.ModuleId == moduleId && mou.UserId == userId).ToListAsync();
}
public async Task<IEnumerable<ModuleOfUser>> ReadByUserAsync(string username) => await ReadByUser(username).ToListAsync();
} }
private IQueryable<ModuleOfUser> ReadByUser(string username)
{
return _dbSet.Where(mou => mou.User!.Username == username).Include(mou => mou.Module);
}
public IQueryable<ModuleOfUser> ReadByModuleId(int moduleId)
{
return _dbSet.Where(mou => mou.ModuleId == moduleId);
}
public async Task<IEnumerable<ModuleOfUser>> ReadByModelUserIdAsync(int moduleId, int userId)
{
return await _dbSet.Where(mou => mou.ModuleId == moduleId && mou.UserId == userId).ToListAsync();
}
public async Task<IEnumerable<ModuleOfUser>> ReadByUserAsync(string username) => await ReadByUser(username).ToListAsync();
} }

View File

@@ -1,15 +1,15 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Repositories namespace DigitalData.UserManager.Infrastructure.Repositories;
public class ModuleRepository<TDbContext> : CRUDRepository<Module, int, TDbContext>, IModuleRepository
where TDbContext : DbContext, IUserManagerDbContext
{ {
public class ModuleRepository<TDbContext> : CRUDRepository<Module, int, TDbContext>, IModuleRepository public ModuleRepository(TDbContext dbContext) : base(dbContext, dbContext.Modules)
where TDbContext : DbContext, IUserManagerDbContext
{ {
public ModuleRepository(TDbContext dbContext) : base(dbContext, dbContext.Modules)
{
}
} }
} }

View File

@@ -1,48 +1,48 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace DigitalData.UserManager.Infrastructure.Repositories namespace DigitalData.UserManager.Infrastructure.Repositories;
public class UserRepRepository<TDbContext> : CRUDRepository<UserRep, int, TDbContext>, IUserRepRepository
where TDbContext : DbContext, IUserManagerDbContext
{ {
public class UserRepRepository<TDbContext> : CRUDRepository<UserRep, int, TDbContext>, IUserRepRepository public UserRepRepository(TDbContext dbContext) : base(dbContext, dbContext.UserReps)
where TDbContext : DbContext, IUserManagerDbContext
{ {
public UserRepRepository(TDbContext dbContext) : base(dbContext, dbContext.UserReps) }
{
}
public async Task<IEnumerable<UserRep>> ReadAllAsync( public async Task<IEnumerable<UserRep>> ReadAllAsync(
bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false, bool withUser = false, bool withRepGroup = false, bool withGroup = false, bool withRepUser = false,
int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true) int? userId = null, int? repUserId = null, int? groupId = null, int? repGroupId = null, bool readOnly = true)
{ {
var query = readOnly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable(); var query = readOnly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withUser) if (withUser)
query = query.Include(ur => ur.User); query = query.Include(ur => ur.User);
if (withRepGroup) if (withRepGroup)
query = query.Include(ur => ur.RepGroup); query = query.Include(ur => ur.RepGroup);
if (withGroup) if (withGroup)
query = query.Include(ur => ur.Group); query = query.Include(ur => ur.Group);
if (withRepUser) if (withRepUser)
query = query.Include(ur => ur.RepUser); query = query.Include(ur => ur.RepUser);
if (userId is not null) if (userId is not null)
query = query.Where(ur => ur.UserId == userId); query = query.Where(ur => ur.UserId == userId);
if (repUserId is not null) if (repUserId is not null)
query = query.Where(ur => ur.RepUserId == repUserId); query = query.Where(ur => ur.RepUserId == repUserId);
if (groupId is not null) if (groupId is not null)
query = query.Where(ur => ur.GroupId == groupId); query = query.Where(ur => ur.GroupId == groupId);
if (repGroupId is not null) if (repGroupId is not null)
query = query.Where(ur => ur.RepGroupId == repGroupId); query = query.Where(ur => ur.RepGroupId == repGroupId);
return await query.ToListAsync(); return await query.ToListAsync();
}
} }
} }

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Infrastructure; using DigitalData.Core.Infrastructure;
using DigitalData.UserManager.Application.Contracts.Repositories;
using DigitalData.UserManager.Domain.Entities; using DigitalData.UserManager.Domain.Entities;
using DigitalData.UserManager.Infrastructure.Contracts; using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;

View File

@@ -11,6 +11,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.UserManager.Inf
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.UserManager.API", "DigitalData.UserManager.API\DigitalData.UserManager.API.csproj", "{07CCD651-647C-49F7-9715-30CEBC13710D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DigitalData.UserManager.API", "DigitalData.UserManager.API\DigitalData.UserManager.API.csproj", "{07CCD651-647C-49F7-9715-30CEBC13710D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.UserManager.DependencyInjection", "DigitalData.UserManager.DependencyInjection\DigitalData.UserManager.DependencyInjection.csproj", "{7E5FD115-EE6D-48F2-ACF1-E951EC071073}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Assets", "Assets", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
ProjectSection(SolutionItems) = preProject
Assets\icon.png = Assets\icon.png
EndProjectSection
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -33,10 +42,17 @@ Global
{07CCD651-647C-49F7-9715-30CEBC13710D}.Debug|Any CPU.Build.0 = Debug|Any CPU {07CCD651-647C-49F7-9715-30CEBC13710D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07CCD651-647C-49F7-9715-30CEBC13710D}.Release|Any CPU.ActiveCfg = Release|Any CPU {07CCD651-647C-49F7-9715-30CEBC13710D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07CCD651-647C-49F7-9715-30CEBC13710D}.Release|Any CPU.Build.0 = Release|Any CPU {07CCD651-647C-49F7-9715-30CEBC13710D}.Release|Any CPU.Build.0 = Release|Any CPU
{7E5FD115-EE6D-48F2-ACF1-E951EC071073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7E5FD115-EE6D-48F2-ACF1-E951EC071073}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E5FD115-EE6D-48F2-ACF1-E951EC071073}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E5FD115-EE6D-48F2-ACF1-E951EC071073}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6804DE1E-B257-4DC9-B9D4-08C8518C8282} SolutionGuid = {6804DE1E-B257-4DC9-B9D4-08C8518C8282}
EndGlobalSection EndGlobalSection