Files
Antifraude.Net/Antifraude.Net/ApiDenuncias/Services/UserComplaintAccessService.cs
Pedro 6f9392f3d0 cambiso en denuncias, mejoras de 30 min en inicio de sesion e incorporacion de grupos
Cambiso de certificado en registro de personal para aañadir el origen
2026-07-27 08:46:41 +02:00

175 lines
6.9 KiB
C#

using System.Globalization;
using MySqlConnector;
namespace ApiDenuncias.Services;
public sealed class UserComplaintAccessService
{
private readonly MySqlConnectionStringProvider _connectionStringProvider;
public UserComplaintAccessService(MySqlConnectionStringProvider connectionStringProvider)
{
_connectionStringProvider = connectionStringProvider;
}
public async Task<HashSet<int>> GetAllowedComplaintIdsAsync(
string username,
CancellationToken cancellationToken = default)
{
var access = await GetComplaintAccessAsync(username, null, cancellationToken);
return access.Keys.ToHashSet();
}
public async Task<IReadOnlyDictionary<int, ComplaintAccessInfo>> GetComplaintAccessAsync(
string username,
IReadOnlyCollection<int>? complaintIds = null,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(username))
{
return new Dictionary<int, ComplaintAccessInfo>();
}
var connectionString = await _connectionStringProvider.GetConnectionStringAsync(cancellationToken);
await using var connection = new MySqlConnection(connectionString);
await connection.OpenAsync(cancellationToken);
await using var command = connection.CreateCommand();
command.Parameters.AddWithValue("@username", username.Trim());
var idFilter = string.Empty;
if (complaintIds is { Count: > 0 })
{
var parameters = new List<string>(complaintIds.Count);
var index = 0;
foreach (var complaintId in complaintIds.Where(id => id > 0).Distinct())
{
var parameterName = $"@complaintId{index++}";
parameters.Add(parameterName);
command.Parameters.AddWithValue(parameterName, complaintId);
}
if (parameters.Count == 0)
{
return new Dictionary<int, ComplaintAccessInfo>();
}
idFilter = $"AND COALESCE(ir.imported_complaint_report_id, ir.progressive_id) IN ({string.Join(", ", parameters)})";
}
command.CommandText = $"""
SELECT
COALESCE(ir.imported_complaint_report_id, ir.progressive_id) AS complaint_id,
owner.username AS owner_username,
CASE WHEN ir.owner_user_id = viewer.id THEN 1 ELSE 0 END AS owned_by_current_user,
CASE
WHEN ir.owner_user_id IS NULL THEN 0
WHEN EXISTS (
SELECT 1
FROM app_user_groups shared_membership
INNER JOIN work_groups active_group
ON active_group.id = shared_membership.work_group_id
AND active_group.is_active = 1
WHERE shared_membership.app_user_id IN (viewer.id, ir.owner_user_id)
GROUP BY shared_membership.work_group_id
HAVING COUNT(DISTINCT shared_membership.app_user_id) = 2
) THEN 1
ELSE 0
END AS accessible_by_group,
(
SELECT GROUP_CONCAT(DISTINCT wg.code ORDER BY wg.code SEPARATOR ',')
FROM app_user_groups owner_membership
INNER JOIN work_groups wg
ON wg.id = owner_membership.work_group_id
AND wg.is_active = 1
WHERE owner_membership.app_user_id = ir.owner_user_id
) AS owner_group_codes
FROM app_users viewer
INNER JOIN inbox_reports ir
ON COALESCE(ir.imported_complaint_report_id, ir.progressive_id) IS NOT NULL
LEFT JOIN app_users owner ON owner.id = ir.owner_user_id
LEFT JOIN user_inbox_reports current_tracking
ON current_tracking.inbox_report_id = ir.id
AND current_tracking.app_user_id = viewer.id
WHERE viewer.username = @username
{idFilter}
AND (
ir.owner_user_id = viewer.id
OR EXISTS (
SELECT 1
FROM app_user_groups shared_membership
INNER JOIN work_groups active_group
ON active_group.id = shared_membership.work_group_id
AND active_group.is_active = 1
WHERE shared_membership.app_user_id IN (viewer.id, ir.owner_user_id)
GROUP BY shared_membership.work_group_id
HAVING COUNT(DISTINCT shared_membership.app_user_id) = 2
)
OR (
ir.owner_user_id IS NULL
AND current_tracking.download_count > 0
)
);
""";
var result = new Dictionary<int, ComplaintAccessInfo>();
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
while (await reader.ReadAsync(cancellationToken))
{
var complaintId = Convert.ToInt32(
reader.GetValue(reader.GetOrdinal("complaint_id")),
CultureInfo.InvariantCulture);
var ownerUsername = reader.IsDBNull(reader.GetOrdinal("owner_username"))
? string.Empty
: reader.GetString(reader.GetOrdinal("owner_username"));
var ownedByCurrentUser =
reader.GetInt32(reader.GetOrdinal("owned_by_current_user")) == 1;
var accessibleByGroup =
reader.GetInt32(reader.GetOrdinal("accessible_by_group")) == 1;
var groupCodes = reader.IsDBNull(reader.GetOrdinal("owner_group_codes"))
? []
: reader.GetString(reader.GetOrdinal("owner_group_codes"))
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
result[complaintId] = new ComplaintAccessInfo(
complaintId,
ownerUsername,
ownedByCurrentUser,
accessibleByGroup,
groupCodes);
}
return result;
}
public async Task<bool> CanAccessComplaintAsync(
string username,
int complaintId,
CancellationToken cancellationToken = default)
{
if (complaintId <= 0)
{
return false;
}
var access = await GetComplaintAccessAsync(
username,
[complaintId],
cancellationToken);
return access.ContainsKey(complaintId);
}
}
public sealed record ComplaintAccessInfo(
int ComplaintId,
string OwnerUsername,
bool OwnedByCurrentUser,
bool AccessibleByGroup,
IReadOnlyList<string> OwnerGroupCodes)
{
public bool RequiresOwnerConfirmation =>
!OwnedByCurrentUser &&
AccessibleByGroup &&
!string.IsNullOrWhiteSpace(OwnerUsername);
}