175 lines
6.9 KiB
C#
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);
|
|
}
|