using adas_core.Application.Exceptions; using adas_core.Application.Repositories.Interfaces; using adas_core.Application.Services.Interfaces; using adas_core.Domain.Enums; using adas_core.Domain.Models.AppSettings; using adas_core.Domain.Models.MongoModels; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MongoDB.Bson; namespace adas_core.Application.Services; public class PermissionService( IOptions permissionsConfig, ILogger logger, Lazy displayService, Lazy userRepository, Lazy authorityRepository) : IPermissionService { private readonly ILogger _logger = logger; private readonly PermissionSettings _permissionsConfig = permissionsConfig.Value; /// /// Retrieves the display permission configuration for the specified user based on their authorization roles. /// Loads the user's authorities from the repository if not already cached, matches the display or its unit against the authorities, and maps the role to the corresponding display permission set (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse). /// /// The display whose permissions are being resolved; matched against the user's authorized display and unit identifiers. /// The user whose authorizations and roles are evaluated to determine the applicable display permissions. /// A containing the display permission configuration corresponding to the matched role. /// Thrown when the user has no authorizations available, or when none of the user's authorities match the given display or its unit. public async Task GetPermissionsForDisplay(Display display, User user) { var authorities = user.Authorization; if (authorities == null) { var a = await userRepository.Value.GetByUserAndAuthoritesName(user.UserName); authorities = a?.Authorization; user.Authorization = authorities; } if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); foreach (var auth in authorities) if (display.Id.ToString().Equals(auth.DisplayId) || display.UnitId.ToString().Equals(auth.UnitId)) { _logger.LogInformation("User {user} has role {role} for display {displayId} or unit {unitId}", user.UserName, auth.Rol, auth.DisplayId, auth.UnitId); //var unitPerms = await GetPermissionsForUnit(display.UnitId.ToString(), user); switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty)) { case PermissionEnum.RolesType.AuthGuest: // return (await CheckPermissions(_permissionsConfig.Guest.Display, display.UnitId.ToString())); return _permissionsConfig.Guest.Display; case PermissionEnum.RolesType.AuthAdmin: // return await CheckPermissions(_permissionsConfig.Admin.Display, display.UnitId.ToString()); return _permissionsConfig.Admin.Display; case PermissionEnum.RolesType.AuthDeveloper: // return await CheckPermissions(_permissionsConfig.Developer.Display, display.UnitId.ToString()); return _permissionsConfig.Developer.Display; case PermissionEnum.RolesType.AuthDoctorchief: // return await CheckPermissions(_permissionsConfig.DoctorChief.Display, display.UnitId.ToString()); return _permissionsConfig.DoctorChief.Display; case PermissionEnum.RolesType.AuthDoctor: // return await CheckPermissions(_permissionsConfig.Doctor.Display, display.UnitId.ToString()); return _permissionsConfig.Doctor.Display; case PermissionEnum.RolesType.AuthNursesupervisor: // return await CheckPermissions(_permissionsConfig.NursingSupervisor.Display, display.UnitId.ToString()); return _permissionsConfig.NursingSupervisor.Display; case PermissionEnum.RolesType.AuthNurse: // return await CheckPermissions(_permissionsConfig.Nurse.Display, display.UnitId.ToString()); return _permissionsConfig.Nurse.Display; } } //return _permissionsConfig.NoPermissions.Display; throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); } /// /// Retrieves the permission set associated with a specific unit for the given user, based on the user's role within that unit. /// Iterates the user's authorizations to find a matching unit, parses the role, and returns the corresponding configured permissions (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse). /// /// The identifier of the unit whose permissions should be resolved. /// The user whose authorizations and role are used to determine the permissions for the unit. /// A that resolves to the permission configuration matching the user's role for the specified unit. /// Thrown when the user has no authorizations, or when no authorization entry matches the provided . public Task GetPermissionsForUnit(string unitId, User user) { var authorities = user.Authorization; if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); foreach (var auth in authorities) if (auth.UnitId != null && auth.UnitId.Equals(unitId)) { _logger.LogInformation("User {user} has role {role} for unit {unitId}", user.UserName, auth.Rol, auth.UnitId); switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty)) { case PermissionEnum.RolesType.AuthGuest: // return await CheckPermissions(_permissionsConfig.Guest.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.Guest.Unit); case PermissionEnum.RolesType.AuthAdmin: // return await CheckPermissions(_permissionsConfig.Admin.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.Admin.Unit); case PermissionEnum.RolesType.AuthDeveloper: // return await CheckPermissions(_permissionsConfig.Developer.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.Developer.Unit); case PermissionEnum.RolesType.AuthDoctorchief: // return await CheckPermissions(_permissionsConfig.DoctorChief.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.DoctorChief.Unit); case PermissionEnum.RolesType.AuthDoctor: // return await CheckPermissions(_permissionsConfig.Doctor.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.Doctor.Unit); case PermissionEnum.RolesType.AuthNursesupervisor: // return await CheckPermissions(_permissionsConfig.NursingSupervisor.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.NursingSupervisor.Unit); case PermissionEnum.RolesType.AuthNurse: // return await CheckPermissions(_permissionsConfig.Nurse.Unit, auth.UnitId); return Task.FromResult(_permissionsConfig.Nurse.Unit); } } throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); } /* private async Task CheckPermissions(DisplayPermissionTypes guestUnit, string authUnitId) { var isParsed = ObjectId.TryParse(authUnitId, out var unitId); if(isParsed) { var unit = await uniService.Value.FindById(unitId); var unitConfiguration = unit?.Configuration; if (unitConfiguration != null) { guestUnit.Admissions.Execute = unitConfiguration.ManualAdmit; guestUnit.Discharges.Execute = unitConfiguration.ManualDischarge; guestUnit.DemographicData.Create = unitConfiguration.ManualEdit; guestUnit.DemographicData.Delete = unitConfiguration.ManualEdit; guestUnit.DemographicData.Update = unitConfiguration.ManualEdit; guestUnit.DemographicData.Execute = unitConfiguration.ManualEdit; } } _logger.LogInformation("Permissions for unit {unitId}: {@permissions}", authUnitId, guestUnit); return guestUnit; } */ /// /// Retrieves the panel permissions for the specified user, including their assigned authorities. /// /// The username of the user whose panel permissions are being requested. /// The that apply to the user based on their authorities. /// Thrown when no user is found with the specified username. public async Task GetPermissionsForPanel(string user) { var userFound = await userRepository.Value.GetByUserName(user); if (userFound == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); userFound.Authorization = await authorityRepository.Value.GetUserAuthorities(userFound.Id); return GetPermissionsForPanel(userFound); } /// /// Retrieves the panel permissions assigned to a user based on their authorization role. /// Iterates through the user's authorities, identifies the first entry with panel authorization, and maps the role to the corresponding configuration (Guest, Admin, Developer, DoctorChief, Doctor, NursingSupervisor, or Nurse). /// /// The user whose panel permissions are being resolved; must carry authorization data. /// The associated with the matched role. /// Thrown when the user has no authorities or no authority grants panel access. public PanelPermissionTypes GetPermissionsForPanel(User user) { var authorities = user.Authorization; if (authorities == null) throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); foreach (var auth in authorities) if (auth.PanelAuthorization) { _logger.LogInformation("User {user} has role {role} for panel", user.UserName, auth.Rol); switch ((PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty)) { case PermissionEnum.RolesType.AuthGuest: return _permissionsConfig.Guest.Panel; case PermissionEnum.RolesType.AuthAdmin: return _permissionsConfig.Admin.Panel; case PermissionEnum.RolesType.AuthDeveloper: return _permissionsConfig.Developer.Panel; case PermissionEnum.RolesType.AuthDoctorchief: return _permissionsConfig.DoctorChief.Panel; case PermissionEnum.RolesType.AuthDoctor: return _permissionsConfig.Doctor.Panel; case PermissionEnum.RolesType.AuthNursesupervisor: return _permissionsConfig.NursingSupervisor.Panel; case PermissionEnum.RolesType.AuthNurse: return _permissionsConfig.Nurse.Panel; } } throw new ForbbidenException(HttpEnum.ErrorMessage.ForbiddenNoPermission); } /// /// Determines whether the specified user has access to a given display, based on the user's role, authorities, and the display record. /// Returns false if the display identifier is not a valid ObjectId, or if either the user or the display cannot be found. /// The user's authorization set is loaded from the authority repository before evaluating access through role checks against the display. /// /// The username used to look up the user attempting to access the display. /// The role of the user, used in the role-based access evaluation against the display. /// The source permission context used when evaluating access to the display. /// The string identifier of the display; must be parseable as an ObjectId or access is denied. /// A task that resolves to true if the user's role and authorities grant access to the specified display; otherwise, false. public async Task HasAccessToDisplay(string username, PermissionEnum.RolesType userRole, PermissionEnum.SourcePermissionsEnum source, string displayId) { if (!ObjectId.TryParse(displayId, out var displayObjectId)) return false; var display = await displayService.Value.GetById(displayObjectId); var user = await userRepository.Value.GetByUserName(username); if (user == null || display == null) return false; var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id); user.Authorization = authorities; return SearchRoleInDisplay(display, authorities, userRole); } /// /// Determines whether the specified user has access to a given unit based on their role and assigned authorities. /// Returns false when the user cannot be found by username; otherwise delegates the final check to the role-in-unit search using the retrieved authorities. /// /// The username of the user whose access is being verified. /// The role type to be matched against the user's authorities for the target unit. /// The source permission context associated with the access evaluation. /// The identifier of the unit to check access against. /// A task that resolves to true if the user has the required access to the unit; otherwise, false. public async Task HasAccessToUnit(string username, PermissionEnum.RolesType userRole, PermissionEnum.SourcePermissionsEnum source, string unitId) { var user = await userRepository.Value.GetByUserName(username); if (user == null) return false; var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id); user.Authorization = authorities; return SearchRoleInUnit(unitId, authorities, userRole); } /// /// Determines whether the specified user has access to a panel by resolving their authorities and checking the required role. Returns false if the user is not found. /// /// The username of the user to check. /// The role required to access the panel. /// The source permission context used for the access check. /// true if the user exists and has the required role within the panel; otherwise, false. public async Task HasAccessToPanel(string username, PermissionEnum.RolesType userRole, PermissionEnum.SourcePermissionsEnum source) { var user = await userRepository.Value.GetByUserName(username); if (user == null) return false; var authorities = await authorityRepository.Value.GetUserAuthorities(user.Id); user.Authorization = authorities; return SearchRoleInPanel(authorities, userRole); } /// /// Determines whether the specified role is present in the authorizations matched by the display's ID, its unit ID, or found via a recursive unit search. /// Returns false when the authorities list is null or empty, and parses each authorization's role string into the enum for comparison. /// /// The display whose identifiers are used to match authorizations. /// The list of authorizations to search; a null or empty value causes the method to return false. /// The role to find within the matched authorizations or the display's unit. /// true if the role is found in any matched authorization or in the display's unit; otherwise, false. private static bool SearchRoleInDisplay(Display display, List? authorities, PermissionEnum.RolesType role) { if (authorities is not { Count: > 0 }) return false; return ( from auth in authorities let authRole = (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty) where display.Id.ToString().Equals(auth.DisplayId) || display.UnitId.ToString().Equals(auth.UnitId) let result = SearchRoleInUnit(display.UnitId.ToString(), authorities, role) where result || authRole == role select authRole ).Any(); } /// /// Searches the supplied authorities list for a specific role assigned to the given unit. Returns false when the authorities list is null, or when no matching entry is found. /// /// The identifier of the unit to match against the authorities entries. /// The list of authorizations to inspect; if null, the method short-circuits and returns false. /// The role type to look for within the authorities of the specified unit. /// true if an authority exists for with a role equal to ; otherwise, false. private static bool SearchRoleInUnit(string unitId, List? authorities, PermissionEnum.RolesType role) { if (authorities == null) return false; foreach (var auth in authorities) { var authRole = (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty); if (auth.UnitId != null && auth.UnitId.Equals(unitId) && authRole == role) return true; } return false; } /// /// Determines whether any authorization entry in the provided list grants the specified role for panel access. /// Returns false when the authorities list is null, when an entry has no role mapping, or when no matching role is found with panel authorization enabled. /// /// The collection of entries to search; may be null. /// The role to look for within the panel-authorized entries. /// true if at least one entry has PanelAuthorization enabled and matches the specified role; otherwise, false. /// Thrown when an entry's Rol value cannot be parsed into a valid . private static bool SearchRoleInPanel(List? authorities, PermissionEnum.RolesType role) { if (authorities == null) return false; return ( from auth in authorities let authRole = (PermissionEnum.RolesType?)Enum.Parse(typeof(PermissionEnum.RolesType), auth.Rol ?? string.Empty) where auth.PanelAuthorization && authRole == role select auth ).Any(); } }