Compare commits
4 Commits
docs/1.0.1
...
3101c53252
| Author | SHA1 | Date | |
|---|---|---|---|
| 3101c53252 | |||
| 481d768ebd | |||
| 255e6f6250 | |||
| ef62b7bfea |
@@ -21,23 +21,20 @@
|
|||||||
|
|
||||||
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
<PackageReference Include="AutoMapper" Version="16.1.1" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.3.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.11" />
|
||||||
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.8" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.8" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.8" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.8" />
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.18.0" />
|
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.3.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.9" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.9.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.9.0" />
|
||||||
<PackageReference Include="Quartz" Version="3.18.1" />
|
<PackageReference Include="Quartz" Version="3.18.1" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.13.17" />
|
<PackageReference Include="StackExchange.Redis" Version="3.0.0" />
|
||||||
<PackageReference Include="System.Text.Json" Version="10.0.8" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="Microsoft.AspNetCore.Http.Features">
|
|
||||||
<HintPath>..\..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\7.0.10\Microsoft.AspNetCore.Http.Features.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -9,12 +9,17 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.10" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.11" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.27" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.8" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.18.0" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.18.0" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.9" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="8.19.1" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -10,17 +10,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.2.0" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.2.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.3.10" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.28" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.3.10" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.27" />
|
|
||||||
<PackageReference Include="MongoDB.Bson" Version="3.9.0" />
|
<PackageReference Include="MongoDB.Bson" Version="3.9.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" />
|
<PackageReference Include="Newtonsoft.Json.Bson" Version="1.0.3" />
|
||||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.49.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.19.1" />
|
||||||
<PackageReference Include="Snappier" Version="1.3.1" />
|
<PackageReference Include="System.Text.Json" Version="10.0.9" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.18.0" />
|
|
||||||
<PackageReference Include="System.Text.Json" Version="10.0.8" />
|
|
||||||
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
<PackageReference Include="TagLibSharp" Version="2.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ namespace adas_core.Infrastructure.Repositories;
|
|||||||
/// options lists, diagnoses, allergies, procedures, treatments, and other reference data.
|
/// options lists, diagnoses, allergies, procedures, treatments, and other reference data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">The type of MasterList entity to manage.</typeparam>
|
/// <typeparam name="T">The type of MasterList entity to manage.</typeparam>
|
||||||
|
/// <!-- aidoc:v1 sig=f958de2 -->
|
||||||
public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository<T> where T : MasterList
|
public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository<T> where T : MasterList
|
||||||
{
|
{
|
||||||
private readonly ApiSettings _apiSettings;
|
private readonly ApiSettings _apiSettings;
|
||||||
@@ -30,6 +31,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="apiSettings">API settings containing collection names configuration.</param>
|
/// <param name="apiSettings">API settings containing collection names configuration.</param>
|
||||||
/// <param name="database">The MongoDB database instance.</param>
|
/// <param name="database">The MongoDB database instance.</param>
|
||||||
/// <exception cref="ArgumentNullException">Thrown when apiSettings is null.</exception>
|
/// <exception cref="ArgumentNullException">Thrown when apiSettings is null.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=800d538 body=d2b18a3 -->
|
||||||
public MasterListRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
|
public MasterListRepository(IOptions<ApiSettings> apiSettings, IMongoDatabase database) : base(database)
|
||||||
{
|
{
|
||||||
if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
|
if (apiSettings == null) throw new ArgumentNullException(nameof(apiSettings));
|
||||||
@@ -41,6 +43,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// Maps different MasterList subtypes to their corresponding MongoDB collection names.
|
/// Maps different MasterList subtypes to their corresponding MongoDB collection names.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The collection name for the current MasterList type.</returns>
|
/// <returns>The collection name for the current MasterList type.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=94e22ff body=64e6c55 -->
|
||||||
public override string GetCollectionName()
|
public override string GetCollectionName()
|
||||||
{
|
{
|
||||||
return typeof(T) switch
|
return typeof(T) switch
|
||||||
@@ -94,6 +97,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the entity to delete.</param>
|
/// <param name="id">The ObjectId of the entity to delete.</param>
|
||||||
/// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
|
/// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=3de1ad6 body=fe0ba8c -->
|
||||||
public async Task Delete(ObjectId id)
|
public async Task Delete(ObjectId id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -109,10 +113,10 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates an existing master list entity with full replacement.
|
/// Replaces the document in the collection matching the entity's identifier, or inserts it when no match exists (upsert behavior). Failures are logged and rethrown to the caller.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entity">The entity with updated values.</param>
|
/// <param name="entity">The entity to upsert; its identifier is used as the filter and <paramref name="entity"/>'s current state replaces the matched document.</param>
|
||||||
/// <exception cref="Exception">Throws and re-throws exceptions after logging.</exception>
|
/// <!-- aidoc:v1 sig=019b691 body=863650a -->
|
||||||
public async Task Update(T entity)
|
public async Task Update(T entity)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -133,6 +137,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The ObjectId of the master list.</param>
|
||||||
/// <param name="newOpt">The OptionList with updated values.</param>
|
/// <param name="newOpt">The OptionList with updated values.</param>
|
||||||
/// <returns>The updated OptionList if found; otherwise, null.</returns>
|
/// <returns>The updated OptionList if found; otherwise, null.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=64ca4dd body=a7714e6 -->
|
||||||
public async Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList newOpt)
|
public async Task<OptionList?> UpdateFullMasterListOption(ObjectId id, OptionList newOpt)
|
||||||
{
|
{
|
||||||
var filter = Builders<T>.Filter.Where(o => o.Id == id && o.Options.Any(opt => opt.Id == newOpt.Id)
|
var filter = Builders<T>.Filter.Where(o => o.Id == id && o.Options.Any(opt => opt.Id == newOpt.Id)
|
||||||
@@ -161,11 +166,11 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds a master list entity by its ID with options projection limited to 100 items.
|
/// Asynchronously retrieves an entity of type <c>T</c> matching the specified <paramref name="id"/>, projecting the "options" array to at most 100 elements. Returns <c>null</c> when no matching document is found or when an exception is caught, with the error logged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the entity to retrieve.</param>
|
/// <param name="id">The <see cref="ObjectId"/> identifier of the entity to locate.</param>
|
||||||
/// <returns>The MasterList entity if found; otherwise, null.</returns>
|
/// <returns>A <see cref="Task{T}"/> that yields the matching entity, or <c>null</c> when the entity is not found or an error occurs.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <!-- aidoc:v1 sig=81ac96a body=1519944 -->
|
||||||
public async Task<T?> FindById(ObjectId id)
|
public async Task<T?> FindById(ObjectId id)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -189,14 +194,15 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds a specific option within a master list by master and option IDs with locale translation.
|
/// Finds a specific option item within a master document using MongoDB aggregation, returning the localized <see cref="OptionList"/> or <c>null</c> if not found.
|
||||||
/// Uses MongoDB aggregation to apply translations and return the translated option.
|
/// Uses the provided <paramref name="locale"/> to resolve translations: when the locale is <see cref="LocaleEnum.Default"/> or matches the document's default locale, the option's main name is returned; otherwise, the matching entry in the option's <c>localeItems</c> is used, falling back to the main name when no translation exists.
|
||||||
|
/// Any exception raised during the aggregation or deserialization is logged and surfaced as a <c>null</c> result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="masterId">The ObjectId of the master list.</param>
|
/// <param name="masterId">The <see cref="ObjectId"/> of the master document that contains the options array.</param>
|
||||||
/// <param name="optionId">The ObjectId of the option to retrieve.</param>
|
/// <param name="optionId">The <see cref="ObjectId"/> of the specific option to retrieve.</param>
|
||||||
/// <param name="locale">The locale for translation.</param>
|
/// <param name="locale">The <see cref="LocaleEnum"/> value used to select the appropriate translation.</param>
|
||||||
/// <returns>The OptionList with translated fields if found; otherwise, null.</returns>
|
/// <returns>A <see cref="Task{OptionList}"/> that resolves to the matching <see cref="OptionList"/>, or <c>null</c> when the option is not found or an error occurs.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <!-- aidoc:v1 sig=fdb2628 body=0e4983e -->
|
||||||
public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale)
|
public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId, LocaleEnum locale)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -342,6 +348,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="optionId">The ObjectId of the option to retrieve.</param>
|
/// <param name="optionId">The ObjectId of the option to retrieve.</param>
|
||||||
/// <returns>The OptionList if found; otherwise, null.</returns>
|
/// <returns>The OptionList if found; otherwise, null.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=ccc34cb body=71837cc -->
|
||||||
public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId)
|
public async Task<OptionList?> FindOptionItemById(ObjectId masterId, ObjectId optionId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -374,6 +381,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="locale">Optional locale for translated option names.</param>
|
/// <param name="locale">Optional locale for translated option names.</param>
|
||||||
/// <returns>The MasterList entity with translated options if found; otherwise, null.</returns>
|
/// <returns>The MasterList entity with translated options if found; otherwise, null.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=cb4a658 body=f2e88d8 -->
|
||||||
public async Task<T?> FindById(ObjectId id, LocaleEnum? locale)
|
public async Task<T?> FindById(ObjectId id, LocaleEnum? locale)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -551,11 +559,11 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds a master list entity by its name.
|
/// Asynchronously retrieves the first entity whose <c>Name</c> matches the supplied <paramref name="name"/>, applying a projection that caps the <c>options</c> array at 100 elements. If no document is found, or the query fails, the task resolves to <c>null</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name of the master list to retrieve.</param>
|
/// <param name="name">The value compared against the entity's <c>Name</c> property to build the equality filter.</param>
|
||||||
/// <returns>The MasterList entity if found; otherwise, null.</returns>
|
/// <returns>A <see cref="Task{T}"/> producing the first matching entity, or <c>null</c> when no match exists or the operation is aborted by a caught exception.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <!-- aidoc:v1 sig=80a1541 body=8b5b558 -->
|
||||||
public async Task<T?> FindByName(string name)
|
public async Task<T?> FindByName(string name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -584,16 +592,18 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The ObjectId of the master list.</param>
|
||||||
/// <param name="textSearch">Optional text to search within options.</param>
|
/// <param name="textSearch">Optional text to search within options.</param>
|
||||||
/// <returns>A list of matching OptionList items.</returns>
|
/// <returns>A list of matching OptionList items.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=569d223 body=bb7f2c6 -->
|
||||||
public async Task<List<OptionList>> GetMasterListByIdAndTextSearchContaining(ObjectId id, string? textSearch)
|
public async Task<List<OptionList>> GetMasterListByIdAndTextSearchContaining(ObjectId id, string? textSearch)
|
||||||
{
|
{
|
||||||
return await GetOptionsByTextSearch(textSearch, id);
|
return await GetOptionsByTextSearch(textSearch, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves paginated master lists with optional text filtering.
|
/// Builds a paginated query against the master collection of <typeparamref name="T"/>, applying a case-insensitive substring match on the <see cref="PaginationFilter.FilteredRequest"/> text against the <c>Name</c> property when provided. Sorts results ascending by <c>name</c> and returns a fluent find interface so callers can continue chaining pagination or projection operations.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filter">The pagination and filtering parameters.</param>
|
/// <param name="filter">The <see cref="PaginationFilter"/> containing pagination options and the optional <see cref="FilteredRequest.Text"/> used to match against <c>Name</c>.</param>
|
||||||
/// <returns>A fluent queryable for MasterList results.</returns>
|
/// <returns>An <see cref="IFindFluent{TDocument, TProjection}"/> representing the sorted (and optionally text-filtered) query, ready for further pagination configuration.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=bbb39c7 body=f42ebaa -->
|
||||||
public IFindFluent<T, T> GetPaginatedMasterList(PaginationFilter filter)
|
public IFindFluent<T, T> GetPaginatedMasterList(PaginationFilter filter)
|
||||||
{
|
{
|
||||||
var filterBuilder = Builders<T>.Filter;
|
var filterBuilder = Builders<T>.Filter;
|
||||||
@@ -640,12 +650,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a new option to a master list.
|
/// Adds a new <see cref="OptionList"/> entry to the master list identified by <paramref name="id"/>, returning <c>null</c> when an equivalent option already exists, the database update does not modify any document, or the operation fails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list (document of type <c>T</c>) that will receive the new option.</param>
|
||||||
/// <param name="opt">The option element to add.</param>
|
/// <param name="opt">The <see cref="FilterOptionListElement"/> describing the option to add, including its name, optional visual properties, and locale information.</param>
|
||||||
/// <returns>The newly created OptionList if successful; otherwise, null if duplicate exists.</returns>
|
/// <returns>A <see cref="Task{T}"/> that yields the newly created <see cref="OptionList"/> when the push update succeeds, or <c>null</c> when a duplicate is detected, no document is modified, or an exception is logged and swallowed.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <!-- aidoc:v1 sig=1602f0d body=e7fd138 -->
|
||||||
public async Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt)
|
public async Task<OptionList?> AddOptionToMasterList(ObjectId id, FilterOptionListElement opt)
|
||||||
{
|
{
|
||||||
var exist = await GetMasterListByIdAndSearchOptions(id, opt);
|
var exist = await GetMasterListByIdAndSearchOptions(id, opt);
|
||||||
@@ -685,10 +695,11 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all master list entities.
|
/// Asynchronously retrieves all entities of type T from the underlying collection.
|
||||||
|
/// If an exception occurs during retrieval, the error is logged and an empty collection is returned as a fallback.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An enumerable of all MasterList entities.</returns>
|
/// <returns>A task that represents the asynchronous operation. The task result contains an <see cref="IEnumerable{T}"/> of all entities, or an empty collection if an error was encountered.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
/// <!-- aidoc:v1 sig=3a61c61 body=40d7129 -->
|
||||||
public async Task<IEnumerable<T>> GetAll()
|
public async Task<IEnumerable<T>> GetAll()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -704,10 +715,13 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all master list entities without options, returning only metadata.
|
/// Retrieves all master lists from the underlying collection and projects each entity into a <see cref="MasterListDto"/>,
|
||||||
|
/// where the <see cref="MasterListDto.Options"/> field holds the count of associated options instead of the full options collection.
|
||||||
|
/// If the operation fails, the exception is logged and an empty collection is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An enumerable of MasterListDto containing id, name, description, listType, and options count.</returns>
|
/// <returns>A task that represents the asynchronous operation, yielding an <see cref="IEnumerable{MasterListDto}"/> of all
|
||||||
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
/// projected master lists, or an empty collection if an error occurs.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=e493435 body=1c299ba -->
|
||||||
public async Task<IEnumerable<MasterListDto>> GetAllWithoutOptions()
|
public async Task<IEnumerable<MasterListDto>> GetAllWithoutOptions()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -732,10 +746,10 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Counts the total number of master list entities in the collection.
|
/// Asynchronously counts all entities in the collection by invoking <c>CountDocumentsAsync</c> with a filter that matches every document. If the operation fails, the exception is logged and the method returns <c>0</c> as a safe fallback.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The total count of entities.</returns>
|
/// <returns>A <see cref="Task{Int32}"/> that yields the total number of entities, or <c>0</c> if an error occurs.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns 0 on failure.</exception>
|
/// <!-- aidoc:v1 sig=f1f0a98 body=01cd3d1 -->
|
||||||
public async Task<int> Count()
|
public async Task<int> Count()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -751,13 +765,13 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches for options within a master list using multiple filter criteria.
|
/// Retrieves the options of a master list identified by <paramref name="id"/>, applying the supplied <paramref name="filters"/> and resolving localized names and descriptions for the requested locale.
|
||||||
/// Uses MongoDB aggregation pipeline to apply filters and locale translations.
|
/// When <paramref name="filters"/> is <c>null</c>, or when the aggregation yields no document, an empty list is returned; any exception is logged and also results in an empty list.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list to retrieve.</param>
|
||||||
/// <param name="filters">The filter criteria including text, name, description, and optionType.</param>
|
/// <param name="filters">The optional <see cref="FilterOptionListElement"/> that defines the search criteria (option type, text, name, and description) and the target locale used to translate each option.</param>
|
||||||
/// <returns>A list of matching OptionList items ordered by name.</returns>
|
/// <returns>A <see cref="Task{List{OptionList}}"/> of <see cref="OptionList"/> entries matching the filters, ordered by name, or an empty list when no results are found.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
/// <!-- aidoc:v1 sig=f6ee5be body=17d6a96 -->
|
||||||
public async Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id, FilterOptionListElement? filters)
|
public async Task<List<OptionList>> GetMasterListByIdAndSearchOptions(ObjectId id, FilterOptionListElement? filters)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -973,6 +987,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="locale">The locale for translation updates.</param>
|
/// <param name="locale">The locale for translation updates.</param>
|
||||||
/// <returns>The updated OptionList if successful; otherwise, null.</returns>
|
/// <returns>The updated OptionList if successful; otherwise, null.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=e771f23 body=5520a4a -->
|
||||||
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt, LocaleEnum locale)
|
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt, LocaleEnum locale)
|
||||||
{
|
{
|
||||||
// 1. Evitar duplicados
|
// 1. Evitar duplicados
|
||||||
@@ -1073,6 +1088,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="newOpt">The OptionList with updated values.</param>
|
/// <param name="newOpt">The OptionList with updated values.</param>
|
||||||
/// <returns>The updated OptionList if successful; otherwise, null.</returns>
|
/// <returns>The updated OptionList if successful; otherwise, null.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=6def54c body=9554543 -->
|
||||||
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt)
|
public async Task<OptionList?> UpdateMasterListOption(ObjectId id, OptionList newOpt)
|
||||||
{
|
{
|
||||||
var master = await FindById(id);
|
var master = await FindById(id);
|
||||||
@@ -1108,6 +1124,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The ObjectId of the master list.</param>
|
||||||
/// <param name="deleteOptId">The ObjectId of the option to delete.</param>
|
/// <param name="deleteOptId">The ObjectId of the option to delete.</param>
|
||||||
/// <returns>True if the option was deleted; otherwise, false.</returns>
|
/// <returns>True if the option was deleted; otherwise, false.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=e663f16 body=75627bc -->
|
||||||
public async Task<bool> DeleteMasterListOption(ObjectId id, ObjectId deleteOptId)
|
public async Task<bool> DeleteMasterListOption(ObjectId id, ObjectId deleteOptId)
|
||||||
{
|
{
|
||||||
// Define el filtro para encontrar el documento por su _id
|
// Define el filtro para encontrar el documento por su _id
|
||||||
@@ -1126,12 +1143,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the metadata details for a master list.
|
/// Updates the option details of an existing master list entry identified by <paramref name="id"/>, applying only the fields from <paramref name="opt"/> that are not <see langword="null"/> (currently <see cref="UpdateMasterListDetailsDto.CanAddElement"/> and <see cref="UpdateMasterListDetailsDto.OptionListDetails"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the document to update in the collection.</param>
|
||||||
/// <param name="opt">The UpdateMasterListDetailsDto with updated values.</param>
|
/// <param name="opt">The <see cref="UpdateMasterListDetailsDto"/> carrying the new values; <see langword="null"/> properties are left unchanged.</param>
|
||||||
/// <returns>The updated UpdateMasterListDetailsDto if successful; otherwise, null.</returns>
|
/// <returns>The <paramref name="opt"/> instance if the document was found and modified; otherwise, <see langword="null"/> when the document was not found, no fields were modified, or the operation failed.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns null on failure.</exception>
|
/// <!-- aidoc:v1 sig=53d4179 body=49f50ca -->
|
||||||
public async Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id,
|
public async Task<UpdateMasterListDetailsDto?> UpdateOptionDetailsToMasterList(ObjectId id,
|
||||||
UpdateMasterListDetailsDto opt)
|
UpdateMasterListDetailsDto opt)
|
||||||
{
|
{
|
||||||
@@ -1161,12 +1178,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the name of a master list.
|
/// Updates the name of a master list entry identified by <paramref name="id"/> in the underlying collection. Returns <c>true</c> when the document was modified, or <c>false</c> if no document matched the identifier or an exception was caught and logged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list document to update.</param>
|
||||||
/// <param name="name">The new name.</param>
|
/// <param name="name">The new name to assign to the master list entry.</param>
|
||||||
/// <returns>True if the update was successful; otherwise, false.</returns>
|
/// <returns><c>true</c> if a document was modified; otherwise, <c>false</c>.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns false on failure.</exception>
|
/// <!-- aidoc:v1 sig=5b79a51 body=36dfe99 -->
|
||||||
public async Task<bool> UpdateMasterListName(ObjectId id, string name)
|
public async Task<bool> UpdateMasterListName(ObjectId id, string name)
|
||||||
{
|
{
|
||||||
var filter = Builders<T>.Filter.And(
|
var filter = Builders<T>.Filter.And(
|
||||||
@@ -1187,12 +1204,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the description of a master list.
|
/// Updates the description of a master list entry identified by <paramref name="id"/> in the MongoDB collection, returning <see langword="true"/> when a document was modified and <see langword="false"/> when no document matched or a caught <see cref="Exception"/> was logged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list entry to update.</param>
|
||||||
/// <param name="description">The new description.</param>
|
/// <param name="description">The new description to set on the entry.</param>
|
||||||
/// <returns>True if the update was successful; otherwise, false.</returns>
|
/// <returns>A <see cref="Task{Boolean}"/> that resolves to <see langword="true"/> if the update modified a document; otherwise, <see langword="false"/> when the entry was not found or an error was logged.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns false on failure.</exception>
|
/// <!-- aidoc:v1 sig=5a27a55 body=3552f46 -->
|
||||||
public async Task<bool> UpdateMasterListDescription(ObjectId id, string description)
|
public async Task<bool> UpdateMasterListDescription(ObjectId id, string description)
|
||||||
{
|
{
|
||||||
var filter = Builders<T>.Filter.And(
|
var filter = Builders<T>.Filter.And(
|
||||||
@@ -1213,12 +1230,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes an option from a master list by matching all its properties.
|
/// Removes the specified option from the master list document identified by <paramref name="id"/> by issuing a pull filter against the MongoDB collection that matches all of the option's properties (name, option type, icons, colors, default flag, and description). Returns true when the document is modified, or false if no match is found or if an exception is caught and logged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list document to update.</param>
|
||||||
/// <param name="oldOpt">The OptionList to remove.</param>
|
/// <param name="oldOpt">The <see cref="OptionList"/> whose property values define the filter used to locate and remove the matching element.</param>
|
||||||
/// <returns>True if the option was removed; otherwise, false.</returns>
|
/// <returns>A <see cref="Task{Boolean}"/> that resolves to true if the document was modified; otherwise, false.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns false on failure.</exception>
|
/// <!-- aidoc:v1 sig=8de50cd body=d974b9b -->
|
||||||
public async Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt)
|
public async Task<bool> RemoveMasterListOption(ObjectId id, OptionList oldOpt)
|
||||||
{
|
{
|
||||||
var filter = Builders<T>.Filter.Eq("_id", id);
|
var filter = Builders<T>.Filter.Eq("_id", id);
|
||||||
@@ -1247,9 +1264,9 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates necessary indexes for the MasterList collection.
|
/// Overrides the base index creation to build non-unique, background MongoDB indexes on the <c>options.name</c>, <c>options.description</c>, and <c>options._id</c> fields of the <see cref="DiagnosisList"/> collection, configuring Spanish as the default and override language for text tokenization. The logic only runs when <typeparamref name="T"/> is <see cref="DiagnosisList"/>; for any other type the method is a no-op.
|
||||||
/// Currently creates text indexes for DiagnosisList on options.name, options.description, and options._id.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <!-- aidoc:v1 sig=4955da2 body=2c8d1f7 -->
|
||||||
public override async Task CreateIndexes()
|
public override async Task CreateIndexes()
|
||||||
{
|
{
|
||||||
if (typeof(T) == typeof(DiagnosisList))
|
if (typeof(T) == typeof(DiagnosisList))
|
||||||
@@ -1275,6 +1292,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="textSearch">Optional text to search within options.</param>
|
/// <param name="textSearch">Optional text to search within options.</param>
|
||||||
/// <returns>A list of matching OptionList items.</returns>
|
/// <returns>A list of matching OptionList items.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=484b092 body=48e3496 -->
|
||||||
public async Task<List<OptionList>> GetMasterListByTextSearch(string? textSearch)
|
public async Task<List<OptionList>> GetMasterListByTextSearch(string? textSearch)
|
||||||
{
|
{
|
||||||
return await GetOptionsByTextSearch(textSearch);
|
return await GetOptionsByTextSearch(textSearch);
|
||||||
@@ -1287,6 +1305,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// <param name="id">Optional master list ObjectId to filter results.</param>
|
/// <param name="id">Optional master list ObjectId to filter results.</param>
|
||||||
/// <returns>A list of matching OptionList items ordered by name.</returns>
|
/// <returns>A list of matching OptionList items ordered by name.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=6ebeac8 body=1d7bbb7 -->
|
||||||
private async Task<List<OptionList>> GetOptionsByTextSearch(string? textSearch, ObjectId? id = null)
|
private async Task<List<OptionList>> GetOptionsByTextSearch(string? textSearch, ObjectId? id = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -1296,7 +1315,6 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
|
|
||||||
// Normaliza el texto de búsqueda a minúsculas
|
// Normaliza el texto de búsqueda a minúsculas
|
||||||
var normalizedTextSearch = textSearch.ToLowerInvariant();
|
var normalizedTextSearch = textSearch.ToLowerInvariant();
|
||||||
// Construye la expresión regular dinámica para tener en cuenta la acentuación en las vocales
|
|
||||||
var regexPattern = BuildRegexPattern(normalizedTextSearch);
|
var regexPattern = BuildRegexPattern(normalizedTextSearch);
|
||||||
// foreach (var c in normalizedTextSearch)
|
// foreach (var c in normalizedTextSearch)
|
||||||
// {
|
// {
|
||||||
@@ -1360,11 +1378,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a fluent query for paginated results with combined filters.
|
/// Creates a find fluent query that applies the provided <paramref name="filters"/> combined with a logical AND, falling back to an empty filter (matching all documents) when no filters are supplied, and orders the results by the specified <paramref name="sort"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filters">List of filter definitions to apply.</param>
|
/// <param name="filters">A list of <see cref="FilterDefinition{T}"/> criteria to combine. When empty, an empty filter is used so that all documents match.</param>
|
||||||
/// <param name="sort">Sort definition for the query results.</param>
|
/// <param name="sort">The <see cref="SortDefinition{T}"/> that defines the ordering of the returned documents.</param>
|
||||||
/// <returns>A fluent queryable for T results.</returns>
|
/// <returns>An <see cref="IFindFluent{T,T}"/> configured with the combined filter and the given sort.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=704d715 body=2a35652 -->
|
||||||
private IFindFluent<T, T> CreateFindFluent(List<FilterDefinition<T>> filters, SortDefinition<T> sort)
|
private IFindFluent<T, T> CreateFindFluent(List<FilterDefinition<T>> filters, SortDefinition<T> sort)
|
||||||
{
|
{
|
||||||
var combinedFilter = filters.Any()
|
var combinedFilter = filters.Any()
|
||||||
@@ -1374,12 +1393,12 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates new locale items for a master list option based on a default locale.
|
/// Creates a new <see cref="Locale"/> instance populated with <see cref="LocaleItem"/> entries for every <see cref="LocaleEnum"/> value except <see cref="LocaleEnum.Default"/> and the locale specified by <paramref name="localeList"/>. Each property of <see cref="Locale"/> matching an included enum name is dynamically assigned a new <see cref="LocaleItem"/> whose <see cref="LocaleItem.Name"/> is set to the value of <paramref name="opt"/>.
|
||||||
/// Creates LocaleItem entries for all locales except the specified default.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="localeList">The default locale to exclude from translations.</param>
|
/// <param name="localeList">The locale to exclude from the generated <see cref="Locale"/> object.</param>
|
||||||
/// <param name="opt">The option name to use as default translation.</param>
|
/// <param name="opt">The name assigned to every <see cref="LocaleItem"/> created in the resulting <see cref="Locale"/>.</param>
|
||||||
/// <returns>A Locale object with translations for all other locales.</returns>
|
/// <returns>A new <see cref="Locale"/> object containing the corresponding <see cref="LocaleItem"/> entries.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=147db59 body=9b63847 -->
|
||||||
private Locale GetNewItemLocale(LocaleEnum localeList, string opt)
|
private Locale GetNewItemLocale(LocaleEnum localeList, string opt)
|
||||||
{
|
{
|
||||||
var newLocale = new Locale();
|
var newLocale = new Locale();
|
||||||
@@ -1419,6 +1438,7 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="input">The input string to build the pattern from.</param>
|
/// <param name="input">The input string to build the pattern from.</param>
|
||||||
/// <returns>A regex-compatible pattern string.</returns>
|
/// <returns>A regex-compatible pattern string.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=2992c00 body=ae88daf -->
|
||||||
private static string BuildRegexPattern(string input)
|
private static string BuildRegexPattern(string input)
|
||||||
{
|
{
|
||||||
var regexPattern = new StringBuilder();
|
var regexPattern = new StringBuilder();
|
||||||
@@ -1452,14 +1472,13 @@ public class MasterListRepository<T> : MongoRepository<T>, IMasterListRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Searches for options within a master list by name with locale translation.
|
/// Retrieves the options of a master list document identified by <paramref name="id"/>, filtering them by the option name <paramref name="newOptName"/> resolved against the specified <paramref name="locale"/>. When the requested locale differs from the document's default locale, the method attempts to use the translated name; if no translation exists, it falls back to the original name. Returns an empty list when no document is found, when no options match, or when an error is logged.
|
||||||
/// Uses MongoDB aggregation pipeline to apply locale-aware filtering.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The ObjectId of the master list.</param>
|
/// <param name="id">The <see cref="ObjectId"/> of the master list document to match in the aggregation pipeline.</param>
|
||||||
/// <param name="newOptName">The option name to search for.</param>
|
/// <param name="newOptName">The option name to filter by after the locale-based name resolution.</param>
|
||||||
/// <param name="locale">The locale for translation.</param>
|
/// <param name="locale">The <see cref="LocaleEnum"/> value used to select the translated name; the matching translation key is derived from its lowercased string representation.</param>
|
||||||
/// <returns>A list of matching OptionList items ordered by name.</returns>
|
/// <returns>A <see cref="Task{T}"/> of <see cref="List{T}"/> of <see cref="OptionList"/> containing the matching options ordered by name, or an empty list when nothing is found or an exception occurs.</returns>
|
||||||
/// <exception cref="Exception">Logs errors and returns empty list on failure.</exception>
|
/// <!-- aidoc:v1 sig=342dc63 body=21f31fc -->
|
||||||
private async Task<List<OptionList>> GetMasterListByIdAndTextSearch(
|
private async Task<List<OptionList>> GetMasterListByIdAndTextSearch(
|
||||||
ObjectId id, string newOptName, LocaleEnum locale)
|
ObjectId id, string newOptName, LocaleEnum locale)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,11 +9,14 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="EasyNetQ" Version="8.1.4" />
|
<PackageReference Include="EasyNetQ" Version="8.1.5" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.8" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.8" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.9" />
|
||||||
<PackageReference Include="MongoMigrations.Core" Version="4.0.15" />
|
<PackageReference Include="MongoMigrations.Core" Version="4.0.15" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.13.17" />
|
<PackageReference Include="StackExchange.Redis" Version="3.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ using adas_core.Domain.Models.MongoModels;
|
|||||||
using adas_core.LdapLogin.Configuration;
|
using adas_core.LdapLogin.Configuration;
|
||||||
using FluentValidation;
|
using FluentValidation;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.HttpResults;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
using Novell.Directory.Ldap;
|
using System.DirectoryServices.Protocols;
|
||||||
|
using System.Net;
|
||||||
|
using Authorization = adas_core.Domain.Models.MongoModels.Authorization;
|
||||||
|
|
||||||
namespace adas_core.LdapLogin;
|
namespace adas_core.LdapLogin;
|
||||||
|
|
||||||
@@ -19,6 +20,7 @@ namespace adas_core.LdapLogin;
|
|||||||
/// The service also handles the creation of new users in the application if they do not already exist, based on the LDAP information.
|
/// The service also handles the creation of new users in the application if they do not already exist, based on the LDAP information.
|
||||||
/// It uses configuration settings for connecting to the LDAP server and for mapping LDAP attributes to user properties and authorities.
|
/// It uses configuration settings for connecting to the LDAP server and for mapping LDAP attributes to user properties and authorities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <!-- aidoc:v1 sig=4e5637f -->
|
||||||
public class LdapLoginService : ILoginService
|
public class LdapLoginService : ILoginService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -50,6 +52,7 @@ public class LdapLoginService : ILoginService
|
|||||||
/// <param name="userService">The user service for managing user information.</param>
|
/// <param name="userService">The user service for managing user information.</param>
|
||||||
/// <param name="authorityService">The authority service for managing user authorities.</param>
|
/// <param name="authorityService">The authority service for managing user authorities.</param>
|
||||||
/// <param name="logger">The logger for logging LDAP login operations.</param>
|
/// <param name="logger">The logger for logging LDAP login operations.</param>
|
||||||
|
/// <!-- aidoc:v1 sig=f1b8e4e body=6c4718f -->
|
||||||
public LdapLoginService(
|
public LdapLoginService(
|
||||||
IOptions<LdapConfig> ldapConfig,
|
IOptions<LdapConfig> ldapConfig,
|
||||||
IValidator<LdapConfig> validator,
|
IValidator<LdapConfig> validator,
|
||||||
@@ -75,115 +78,106 @@ public class LdapLoginService : ILoginService
|
|||||||
public UserEnum.LoginMethod Method => UserEnum.LoginMethod.Ldap;
|
public UserEnum.LoginMethod Method => UserEnum.LoginMethod.Ldap;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Authenticates a user against the LDAP server using the provided username and password.
|
/// Authenticates a user against the configured LDAP server using <paramref name="username"/> and <paramref name="password"/>, and resolves the matching <see cref="User"/> entry. When an LDAP service account is configured it is used for the bind; otherwise the username is optionally prefixed with the configured domain and bound with the supplied password. Searches the directory for the user entry and creates or retrieves the local <see cref="User"/> record.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">The username of the user to authenticate.</param>
|
/// <param name="username">The username used to bind to LDAP and to locate the user entry in the directory.</param>
|
||||||
/// <param name="password">The password of the user to authenticate.</param>
|
/// <param name="password">The password used for the LDAP bind when no service account is configured.</param>
|
||||||
/// <returns>The authenticated user.</returns>
|
/// <returns>The <see cref="User"/> resolved from the LDAP directory entry.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when there is an error during the login process.</exception>
|
/// <exception cref="LoginServicesException">Thrown when the LDAP server is not configured, when the directory entry is not found, or when the resolved user is null.</exception>
|
||||||
/// <exception cref="UserNotFoundException">Thrown when the user is not found in the LDAP directory.</exception>
|
/// <exception cref="UserNotFoundException">Thrown when the LDAP bind fails for the supplied credentials.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=bdfb451 body=e3fc1ac -->
|
||||||
public async Task<User> Login(string username, string password)
|
public async Task<User> Login(string username, string password)
|
||||||
{
|
{
|
||||||
// Check for LDAP config
|
if (_ldapConfig.Server == null)
|
||||||
if (_ldapConfig.Server == null) throw new LoginServicesException("LDAP Config not found");
|
throw new LoginServicesException("LDAP Config not found");
|
||||||
var conn = new LdapConnection();
|
|
||||||
|
var identifier = new LdapDirectoryIdentifier(_ldapConfig.Server, _ldapConfig.Port ?? 389);
|
||||||
|
var connection = new LdapConnection(identifier);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await conn.ConnectAsync(_ldapConfig.Server, _ldapConfig.Port ?? 389);
|
if (_ldapConfig.LdapUser != null)
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
_logger.LogError("[LDAP] Error connecting to {server}, port {port}, Exception: {e}", _ldapConfig.Server,
|
|
||||||
_ldapConfig.Port, e.Message);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ldapConfig.LdapUser != null)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("[LDAP] _ldapConfig.LdapUser is enabled with {LdapUser}", _ldapConfig.LdapUser);
|
|
||||||
await conn.BindAsync(_ldapConfig.LdapUser, _ldapConfig.LdapPassword);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var ldapUser = (!string.IsNullOrEmpty(_ldapConfig.UserDomainName)
|
|
||||||
? _ldapConfig.UserDomainName + @"\"
|
|
||||||
: "") + username;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
await conn.BindAsync(ldapUser, password);
|
_logger.LogInformation("[LDAP] Using configured LDAP user {LdapUser}", _ldapConfig.LdapUser);
|
||||||
|
connection.Credential = new NetworkCredential(_ldapConfig.LdapUser, _ldapConfig.LdapPassword);
|
||||||
}
|
}
|
||||||
catch (LdapException e)
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError("[LDAP] Error binding ldapUser: {LdapUser} and password", ldapUser);
|
var ldapUser = (!string.IsNullOrEmpty(_ldapConfig.UserDomainName)
|
||||||
|
? _ldapConfig.UserDomainName + @"\"
|
||||||
|
: "") + username;
|
||||||
|
|
||||||
throw new UserNotFoundException(username, e);
|
connection.Credential = new NetworkCredential(ldapUser, password);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var results = await conn.SearchAsync(
|
|
||||||
_ldapConfig.SearchBase,
|
|
||||||
LdapConnection.ScopeSub,
|
|
||||||
$"({_ldapConfig.UserNameProperty}={username})",
|
|
||||||
null,
|
|
||||||
false);
|
|
||||||
|
|
||||||
|
|
||||||
LdapEntry? entry = null;
|
|
||||||
|
|
||||||
while (await results.HasMoreAsync())
|
|
||||||
{
|
|
||||||
LdapEntry? current = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
current = await results.NextAsync();
|
|
||||||
}
|
|
||||||
catch (LdapException ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning("[LDAP] Skipping invalid entry: {error}", ex.Message);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current != null)
|
connection.AuthType = AuthType.Basic;
|
||||||
|
connection.Bind();
|
||||||
|
}
|
||||||
|
catch (LdapException e)
|
||||||
|
{
|
||||||
|
_logger.LogError("[LDAP] Error binding user {username}", username);
|
||||||
|
throw new UserNotFoundException(username, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResultEntry? entry = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var request = new SearchRequest(
|
||||||
|
_ldapConfig.SearchBase,
|
||||||
|
$"({_ldapConfig.UserNameProperty}={username})",
|
||||||
|
SearchScope.Subtree
|
||||||
|
);
|
||||||
|
|
||||||
|
var response = (SearchResponse)connection.SendRequest(request);
|
||||||
|
|
||||||
|
foreach (SearchResultEntry current in response.Entries)
|
||||||
{
|
{
|
||||||
entry = current;
|
entry = current;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError("[LDAP] Search error for user {username}: {error}", username, e.Message);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == null)
|
||||||
|
throw new LoginServicesException("LDAP User not found");
|
||||||
|
|
||||||
if (entry == null) throw new LoginServicesException("LDAP User not found");
|
|
||||||
var userEntryLdap = GetUser(entry);
|
var userEntryLdap = GetUser(entry);
|
||||||
var user = await GetOrCreateUser(userEntryLdap, entry);
|
var user = await GetOrCreateUser(userEntryLdap, entry);
|
||||||
_logger.LogInformation("[LDAP] entry is {entry} and user {user}", entry, user);
|
|
||||||
// user.Authorization.AddRange(GetAuthorities(entry));
|
_logger.LogInformation("[LDAP] entry found and user {user}", user);
|
||||||
conn.Disconnect();
|
|
||||||
|
connection.Dispose();
|
||||||
|
|
||||||
return user ?? throw new LoginServicesException("LDAP User not found");
|
return user ?? throw new LoginServicesException("LDAP User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the login process is handled through the Login(string username, string password) method.
|
/// Authenticates a user based on the current <see cref="HttpContext"/>.
|
||||||
|
/// This method is not yet implemented and always throws a <see cref="LoginServicesException"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The HTTP context of the request.</param>
|
/// <param name="context">The <see cref="HttpContext"/> of the incoming HTTP request used to extract authentication information.</param>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task{TResult}"/> that resolves to the authenticated user.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Always thrown because the method is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=851dc90 -->
|
||||||
public Task<User> Login(HttpContext context)
|
public Task<User> Login(HttpContext context)
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the authentication process is handled through the Login(string username, string password) method.
|
/// Authenticates a user with the provided <paramref name="username"/> and <paramref name="password"/> and returns the corresponding <see cref="User"/>.
|
||||||
|
/// This method is not implemented and always throws a <see cref="LoginServicesException"/> when invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username">The username of the user to authenticate.</param>
|
/// <param name="username">The username of the user to authenticate.</param>
|
||||||
/// <param name="password">The password of the user to authenticate.</param>
|
/// <param name="password">The password of the user to authenticate.</param>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task{User}"/> that yields the authenticated <see cref="User"/>.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Thrown because authentication is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=a0b1f46 -->
|
||||||
public Task<User> Authenticate(string username, string password)
|
public Task<User> Authenticate(string username, string password)
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
||||||
@@ -191,10 +185,9 @@ public class LdapLoginService : ILoginService
|
|||||||
/// <param name="id">The ID of the user to retrieve.</param>
|
/// <param name="id">The ID of the user to retrieve.</param>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=877a869 -->
|
||||||
public Task<User?> GetById(ObjectId id)
|
public Task<User?> GetById(ObjectId id)
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
||||||
@@ -202,10 +195,9 @@ public class LdapLoginService : ILoginService
|
|||||||
/// <param name="email">The email of the user to retrieve.</param>
|
/// <param name="email">The email of the user to retrieve.</param>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=ba92b09 -->
|
||||||
public Task<User?> GetByEmail(string email)
|
public Task<User?> GetByEmail(string email)
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
||||||
@@ -213,20 +205,19 @@ public class LdapLoginService : ILoginService
|
|||||||
/// <param name="username">The username of the user to retrieve.</param>
|
/// <param name="username">The username of the user to retrieve.</param>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=5184c30 -->
|
||||||
public Task<User?> GetByUsername(string username)
|
public Task<User?> GetByUsername(string username)
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
/// This method is not implemented in the LdapLoginService, as the user retrieval process is handled through the Login(string username, string password) method and the GetOrCreateUser(User userEntryLdap, LdapEntry entry) method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A task representing the asynchronous operation.</returns>
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
/// <exception cref="LoginServicesException">Thrown when the method is not implemented.</exception>
|
||||||
|
/// <!-- aidoc:v1 sig=aa066d6 -->
|
||||||
public Task<List<User>> GetAllUsers()
|
public Task<List<User>> GetAllUsers()
|
||||||
{
|
=> throw new LoginServicesException("Not implemented");
|
||||||
throw new LoginServicesException("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method retrieves an existing user from the application based on the information obtained from the LDAP entry, or creates a new user if one does not already exist.
|
/// This method retrieves an existing user from the application based on the information obtained from the LDAP entry, or creates a new user if one does not already exist.
|
||||||
@@ -235,55 +226,55 @@ public class LdapLoginService : ILoginService
|
|||||||
/// <param name="userEntryLdap">The user information obtained from the LDAP entry.</param>
|
/// <param name="userEntryLdap">The user information obtained from the LDAP entry.</param>
|
||||||
/// <param name="entry">The LDAP entry containing the user's information.</param>
|
/// <param name="entry">The LDAP entry containing the user's information.</param>
|
||||||
/// <returns>The existing or newly created user with updated authorities.</returns>
|
/// <returns>The existing or newly created user with updated authorities.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=87de1c2 body=a43bebd -->
|
||||||
private async Task<User?> GetOrCreateUser(User userEntryLdap, LdapEntry entry)
|
private async Task<User?> GetOrCreateUser(User userEntryLdap, LdapEntry entry)
|
||||||
|
|
||||||
{
|
{
|
||||||
var userToReturn = (await _userService.Value.GetUserByUserName(userEntryLdap.UserName) ??
|
var userToReturn = (await _userService.Value.GetUserByUserName(userEntryLdap.UserName) ??
|
||||||
await _userService.Value.GetUserByName(userEntryLdap.Name)) ??
|
await _userService.Value.GetUserByName(userEntryLdap.Name)) ??
|
||||||
await _userService.Value.CreateUser(userEntryLdap);
|
await _userService.Value.CreateUser(userEntryLdap);
|
||||||
|
|
||||||
if (userToReturn == null) return userToReturn;
|
if (userToReturn == null) return userToReturn;
|
||||||
|
|
||||||
userToReturn.Authorization = [];
|
userToReturn.Authorization = [];
|
||||||
|
|
||||||
var authorities = await CheckAuthorities(userToReturn, entry);
|
var authorities = await CheckAuthorities(userToReturn, entry);
|
||||||
|
|
||||||
userToReturn.Authorization.AddRange(authorities);
|
userToReturn.Authorization.AddRange(authorities);
|
||||||
//foreach (var authorization in userToReturn.Authorization)
|
|
||||||
//{
|
|
||||||
// Enum.TryParse<RolesType>(authorization.Rol, out var compareRole);
|
|
||||||
// if (compareRole == RolesType.Admin) userToReturn.Rol = RolesType.Admin;
|
|
||||||
//}
|
|
||||||
return userToReturn;
|
return userToReturn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method checks the authorities of a user based on the LDAP entry and the application's configuration.
|
/// Synchronizes the authorities of a <paramref name="user"/> with the authorities defined in the supplied <paramref name="entry"/>, updating existing records or inserting new ones as appropriate.
|
||||||
|
/// Authorities appearing in the whitelist are preserved and excluded from the LDAP map, while remaining LDAP authorities are compared against the current persisted list; entries with <see cref="Authorization.CanUpdate"/> set to <c>true</c> have their <see cref="Authorization.Rol"/> refreshed via <see cref="AuthorityService.updateOne"/>, otherwise they are inserted with <see cref="AuthorityService.InsertOne"/>.
|
||||||
|
/// On any failure, the error is logged and an empty list is returned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user whose authorities are being checked.</param>
|
/// <param name="user">The user whose authorities are being reconciled.</param>
|
||||||
/// <param name="entry">The LDAP entry containing the user's information.</param>
|
/// <param name="entry">The <see cref="LdapEntry"/> providing the source authorities map and whitelist.</param>
|
||||||
/// <returns>A list of updated authorities for the user.</returns>
|
/// <returns>The updated <see cref="Authorization"/> list retrieved from the authority service.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=d2bafd5 body=4af15eb -->
|
||||||
private async Task<List<Authorization>> CheckAuthorities(User user, LdapEntry entry)
|
private async Task<List<Authorization>> CheckAuthorities(User user, LdapEntry entry)
|
||||||
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var authorizationMap = GetAuthoritiesMap(entry, user);
|
var authorizationMap = GetAuthoritiesMap(entry, user);
|
||||||
var authorizationWhiteList = GetAuthoritiesWhiteList(entry, user);
|
var authorizationWhiteList = GetAuthoritiesWhiteList(entry, user);
|
||||||
_logger.LogInformation(
|
|
||||||
"[LDAP] user {user} authorizationMap count is {authorizationMap}, authorizationWhiteList count is {authorizationWhiteList}",
|
|
||||||
user.UserName, authorizationMap.Count, authorizationWhiteList.Count);
|
|
||||||
|
|
||||||
// Primero, creamos un HashSet con los DisplayID de la lista blanca para búsqueda eficiente
|
|
||||||
var whiteListDisplayIds = new HashSet<string?>(authorizationWhiteList.Select(a => a.DisplayId));
|
var whiteListDisplayIds = new HashSet<string?>(authorizationWhiteList.Select(a => a.DisplayId));
|
||||||
|
|
||||||
// Filtramos los elementos de authorizationMap que no están en la lista blanca, basándonos en DisplayID
|
var uniqueMapAuthorizations = authorizationMap
|
||||||
var uniqueMapAuthorizations = authorizationMap.Where(a => !whiteListDisplayIds.Contains(a.DisplayId));
|
.Where(a => !whiteListDisplayIds.Contains(a.DisplayId));
|
||||||
|
|
||||||
// Finalmente, combinamos los elementos únicos de authorizationMap con los de authorizationWhiteList
|
|
||||||
var combinedList = authorizationWhiteList.Concat(uniqueMapAuthorizations).ToList();
|
var combinedList = authorizationWhiteList.Concat(uniqueMapAuthorizations).ToList();
|
||||||
|
|
||||||
var userAuthorities = await _authorityService.GetUserAuthorities(user.Id);
|
var userAuthorities = await _authorityService.GetUserAuthorities(user.Id);
|
||||||
|
|
||||||
foreach (var auth in combinedList)
|
foreach (var auth in combinedList)
|
||||||
{
|
{
|
||||||
var authFound = userAuthorities.Find(c => c.DisplayId == auth.DisplayId);
|
var authFound = userAuthorities.Find(c => c.DisplayId == auth.DisplayId);
|
||||||
|
|
||||||
if (authFound is { CanUpdate: true })
|
if (authFound is { CanUpdate: true })
|
||||||
{
|
{
|
||||||
authFound.Rol = auth.Rol;
|
authFound.Rol = auth.Rol;
|
||||||
@@ -299,42 +290,41 @@ public class LdapLoginService : ILoginService
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError("[LDAP] CheckAuthorities for user {user} has exception {ex}", user.UserName, e.Message);
|
_logger.LogError("[LDAP] CheckAuthorities error for user {user}: {error}",
|
||||||
|
user.UserName, e.Message);
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method retrieves a list of authorities for a user based on a whitelist defined in the application's configuration.
|
/// This method retrieves a list of authorities for a user based on a whitelist defined in the application's configuration.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="entry">The LDAP entry containing the user's information.</param>
|
/// <param name="entry">The LDAP entry containing the user's information.</param>
|
||||||
/// <param name="user">The user whose authorities are being retrieved.</param>
|
/// <param name="user">The user whose authorities are being retrieved.</param>
|
||||||
/// <returns>A list of authorities for the user based on the whitelist.</returns>
|
/// <returns>A list of authorities for the user based on the whitelist.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=a8263f0 body=9e6cc38 -->
|
||||||
private List<Authorization> GetAuthoritiesWhiteList(LdapEntry entry, User user)
|
private List<Authorization> GetAuthoritiesWhiteList(LdapEntry entry, User user)
|
||||||
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var userWhiteList = new List<Authorization>();
|
var result = new List<Authorization>();
|
||||||
var userNameProperty = _ldapConfig.UserNameProperty;
|
|
||||||
|
|
||||||
var whiteList = _ldapConfig.WhiteList.FindAll(u =>
|
var whiteList = _ldapConfig.WhiteList.FindAll(u =>
|
||||||
(u.Name != null && entry.Dn.Contains(u.Name, StringComparison.CurrentCultureIgnoreCase)) ||
|
(u.Name != null && entry.DistinguishedName.Contains(u.Name, StringComparison.CurrentCultureIgnoreCase)) ||
|
||||||
(u.Username != null &&
|
(u.Username != null &&
|
||||||
userNameProperty != null &&
|
entry.Attributes[_ldapConfig.UserNameProperty]?[0]?.ToString()
|
||||||
entry.GetAttributeSet().TryGetValue(userNameProperty, out var attr) &&
|
?.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase) == true)
|
||||||
attr.StringValue != null &&
|
|
||||||
attr.StringValue.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase))
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (whiteList.Count == 0) return userWhiteList;
|
|
||||||
|
|
||||||
foreach (var authorityMap in whiteList)
|
foreach (var authorityMap in whiteList)
|
||||||
{
|
{
|
||||||
if (!Enum.TryParse<PermissionEnum.RolesType>(authorityMap.Rol, out _))
|
if (!Enum.TryParse<PermissionEnum.RolesType>(authorityMap.Rol, out _))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
userWhiteList.Add(new Authorization
|
result.Add(new Authorization
|
||||||
{
|
{
|
||||||
UserId = user.Id,
|
UserId = user.Id,
|
||||||
DisplayId = authorityMap.DisplayId,
|
DisplayId = authorityMap.DisplayId,
|
||||||
@@ -342,70 +332,61 @@ public class LdapLoginService : ILoginService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return userWhiteList;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError("[LDAP] GetAuthoritiesWhiteList for user {user} has exception {ex}", user.UserName,
|
_logger.LogError("[LDAP] GetAuthoritiesWhiteList error: {error}", e.Message);
|
||||||
e.Message);
|
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method retrieves a list of authorities for a user based on the LDAP entry and the application's configuration for mapping LDAP groups to authorities.
|
/// Maps the supplied <see cref="LdapEntry"/> to a <see cref="User"/> instance by reading attributes whose names are configured in the LDAP settings.
|
||||||
|
/// The <see cref="User.UserName"/> is taken from the configured user name property, falling back to an empty string when the attribute is absent.
|
||||||
|
/// When the first name and/or last name properties are configured, their attribute values are used to build the <see cref="User.Name"/>; otherwise the name is left unset, and the last name is appended to the first name when both are present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ldapEntry">The LDAP entry containing the user's information.</param>
|
/// <param name="ldapEntry">The <see cref="LdapEntry"/> whose attributes are read to populate the resulting <see cref="User"/>.</param>
|
||||||
/// <returns>The existing or newly created user with updated authorities.</returns>
|
/// <returns>A <see cref="User"/> populated from the attributes exposed by <paramref name="ldapEntry"/> according to the current LDAP configuration.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=947181a body=33ffec8 -->
|
||||||
private User GetUser(LdapEntry ldapEntry)
|
private User GetUser(LdapEntry ldapEntry)
|
||||||
|
|
||||||
{
|
{
|
||||||
var attributes = ldapEntry.GetAttributeSet();
|
var user = new User
|
||||||
|
|
||||||
var user = new User();
|
|
||||||
|
|
||||||
// UserName
|
|
||||||
if (!string.IsNullOrWhiteSpace(_ldapConfig.UserNameProperty) &&
|
|
||||||
attributes.TryGetValue(_ldapConfig.UserNameProperty, out var userAttr) &&
|
|
||||||
userAttr?.StringValue != null)
|
|
||||||
{
|
{
|
||||||
user.UserName = userAttr.StringValue;
|
UserName = entry.Attributes[_ldapConfig.UserNameProperty]?[0]?.ToString() ?? ""
|
||||||
}
|
};
|
||||||
else
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(_ldapConfig.FirstNameProperty))
|
||||||
{
|
{
|
||||||
user.UserName = "";
|
var first = entry.Attributes[_ldapConfig.FirstNameProperty]?[0]?.ToString();
|
||||||
|
if (first != null)
|
||||||
|
user.Name = first;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("[LDAP] GetUser UserName is {UserName} ", user.UserName);
|
if (!string.IsNullOrWhiteSpace(_ldapConfig.LastNameProperty))
|
||||||
|
|
||||||
// First name
|
|
||||||
if (!string.IsNullOrWhiteSpace(_ldapConfig.FirstNameProperty) &&
|
|
||||||
attributes.TryGetValue(_ldapConfig.FirstNameProperty, out var firstNameAttr) &&
|
|
||||||
firstNameAttr?.StringValue != null)
|
|
||||||
{
|
{
|
||||||
user.Name = firstNameAttr.StringValue;
|
var last = entry.Attributes[_ldapConfig.LastNameProperty]?[0]?.ToString();
|
||||||
}
|
if (last != null)
|
||||||
|
user.Name = string.IsNullOrEmpty(user.Name)
|
||||||
// Last name
|
? last
|
||||||
if (!string.IsNullOrWhiteSpace(_ldapConfig.LastNameProperty) &&
|
: $"{user.Name} {last}";
|
||||||
attributes.TryGetValue(_ldapConfig.LastNameProperty, out var lastNameAttr) &&
|
|
||||||
lastNameAttr?.StringValue != null)
|
|
||||||
{
|
|
||||||
user.Name = string.IsNullOrEmpty(user.Name)
|
|
||||||
? lastNameAttr.StringValue
|
|
||||||
: $"{user.Name} {lastNameAttr.StringValue}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method retrieves a list of authorities for a user based on the LDAP entry and the application's configuration for mapping LDAP groups to authorities.
|
/// This method retrieves a list of authorities for a user based on the LDAP entry and the application's configuration for mapping LDAP groups to authorities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ldapEntry">The LDAP entry containing the user's information.</param>
|
/// <param name="ldapEntry">The LDAP entry containing the user's information.</param>
|
||||||
/// <param name="user">The user whose authorities are being retrieved.</param>
|
/// <param name="user">The user whose authorities are being retrieved.</param>
|
||||||
/// <returns>A list of authorities for the user based on the LDAP entry and the application's configuration.</returns>
|
/// <returns>A list of authorities for the user based on the LDAP entry and the application's configuration.</returns>
|
||||||
|
/// <!-- aidoc:v1 sig=1e1f0f2 body=18015c0 -->
|
||||||
private List<Authorization> GetAuthoritiesMap(LdapEntry ldapEntry, User user)
|
private List<Authorization> GetAuthoritiesMap(LdapEntry ldapEntry, User user)
|
||||||
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -414,16 +395,14 @@ public class LdapLoginService : ILoginService
|
|||||||
if (!_ldapConfig.AuthoritiesMap.Any())
|
if (!_ldapConfig.AuthoritiesMap.Any())
|
||||||
return authorities;
|
return authorities;
|
||||||
|
|
||||||
var attributes = ldapEntry.GetAttributeSet();
|
var groupAttr = entry.Attributes[_ldapConfig.GroupsProperty];
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_ldapConfig.GroupsProperty) ||
|
if (groupAttr == null)
|
||||||
!attributes.TryGetValue(_ldapConfig.GroupsProperty!, out var groupsAttr) ||
|
|
||||||
groupsAttr?.StringValueArray == null)
|
|
||||||
{
|
|
||||||
return authorities;
|
return authorities;
|
||||||
}
|
|
||||||
|
|
||||||
var groups = groupsAttr.StringValueArray.ToList();
|
var groups = groupAttr.GetValues(typeof(string))
|
||||||
|
.Cast<string>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
foreach (var authorityMap in _ldapConfig.AuthoritiesMap)
|
foreach (var authorityMap in _ldapConfig.AuthoritiesMap)
|
||||||
{
|
{
|
||||||
@@ -446,7 +425,7 @@ public class LdapLoginService : ILoginService
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_logger.LogError("Error getting Authorities Map. Return new empty list. Exception: {e}", e);
|
_logger.LogError("[LDAP] GetAuthoritiesMap error: {error}", e.Message);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,12 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
||||||
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="4.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.11" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="System.DirectoryServices.Protocols" Version="10.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,13 +8,17 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\adas-core.Authentication\adas-core.Authentication.csproj"/>
|
<ProjectReference Include="..\adas-core.Authentication\adas-core.Authentication.csproj" />
|
||||||
<ProjectReference Include="..\adas-core.Infrastructure\adas-core.Infrastructure.csproj"/>
|
<ProjectReference Include="..\adas-core.Infrastructure\adas-core.Infrastructure.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0"/>
|
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.11" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -21,14 +21,18 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.28" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
|
||||||
<PackageReference Include="Microsoft.QualityTools.Testing.Fakes" Version="18.1.1" />
|
|
||||||
<PackageReference Include="Mongo2Go" Version="4.1.0" />
|
<PackageReference Include="Mongo2Go" Version="4.1.0" />
|
||||||
<PackageReference Include="MongoMigrations.Core" Version="4.0.15" />
|
<PackageReference Include="MongoMigrations.Core" Version="4.0.15" />
|
||||||
<PackageReference Include="Moq" Version="4.20.72" />
|
<PackageReference Include="Moq" Version="4.20.72" />
|
||||||
<PackageReference Include="NUnit" Version="4.6.1" />
|
<PackageReference Include="NUnit" Version="4.6.1" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="6.2.0" />
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="4.13.0">
|
<PackageReference Include="NUnit.Analyzers" Version="4.14.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
@@ -14,6 +14,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
@@ -15,11 +15,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.8" />
|
|
||||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.49.0" />
|
|
||||||
<PackageReference Include="Snappier" Version="1.3.1" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.8" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ using adas_core.WebSocket;
|
|||||||
using adas_core.WebSocket.Hubs;
|
using adas_core.WebSocket.Hubs;
|
||||||
using audit_logs.Extensions;
|
using audit_logs.Extensions;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi;
|
||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -120,7 +120,7 @@ builder.Services.AddSwaggerGen(c =>
|
|||||||
|
|
||||||
c.MapType<ObjectId>(() => new OpenApiSchema
|
c.MapType<ObjectId>(() => new OpenApiSchema
|
||||||
{
|
{
|
||||||
Type = "string",
|
Type = JsonSchemaType.String,
|
||||||
Format = "objectid"
|
Format = "objectid"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+11
-14
@@ -24,15 +24,17 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
<PackageReference Include="AuditLogs" Version="1.0.59" />
|
||||||
<PackageReference Include="EasyNetQ" Version="8.1.4" />
|
<PackageReference Include="EasyNetQ" Version="8.1.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.28" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="5.3.0" />
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.3.0">
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.27" />
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<PackageReference Include="Microsoft.IdentityModel.Protocols" Version="8.18.0" />
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Analyzers" Version="8.0.28" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Protocols" Version="8.19.1" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.9.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.9.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
|
||||||
<PackageReference Include="Quartz" Version="3.18.1" />
|
|
||||||
<PackageReference Include="Serilog" Version="4.3.1" />
|
<PackageReference Include="Serilog" Version="4.3.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Dynamic" Version="1.0.9" />
|
<PackageReference Include="Serilog.Enrichers.Dynamic" Version="1.0.9" />
|
||||||
@@ -42,18 +44,13 @@
|
|||||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||||
|
|
||||||
<PackageReference Include="System.Diagnostics.PerformanceCounter" Version="10.0.8" />
|
|
||||||
<PackageReference Include="System.Management" Version="10.0.8" />
|
|
||||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
|
||||||
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
|
|
||||||
<PackageReference Include="WebPush" Version="1.0.13" />
|
<PackageReference Include="WebPush" Version="1.0.13" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.1" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="10.2.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.27" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.28" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
//"ConnectionString": "mongodb://smartuci:!H12o2020@localhost:27017/?authSource=smartuci",
|
//"ConnectionString": "mongodb://smartuci:!H12o2020@localhost:27017/?authSource=smartuci",
|
||||||
//"ConnectionString": "mongodb://admin:4q*pHca@10.0.20.78:22007",
|
//"ConnectionString": "mongodb://admin:4q*pHca@10.0.20.78:22007",
|
||||||
//"ConnectionString": "mongodb://smacsuci:2(R*aQpu2r@sumo.julianrojas.xyz:27017/?authSource=admin",
|
//"ConnectionString": "mongodb://smacsuci:2(R*aQpu2r@sumo.julianrojas.xyz:27017/?authSource=admin",
|
||||||
"DatabaseName": "adasDevNewStandard"
|
"DatabaseName": "adas"
|
||||||
},
|
},
|
||||||
"DatabaseConfigurationAudit": {
|
"DatabaseConfigurationAudit": {
|
||||||
"ConnectionString": "mongodb://smacsuci:2(R*aQpu2r@julianrojas.xyz:27017/?authSource=admin",
|
"ConnectionString": "mongodb://smacsuci:2(R*aQpu2r@julianrojas.xyz:27017/?authSource=admin",
|
||||||
|
|||||||
Reference in New Issue
Block a user