292 lines
9.7 KiB
C#
292 lines
9.7 KiB
C#
using adas_core.Authentication.Interfaces;
|
|
using adas_core.Domain.Enums;
|
|
using adas_core.Domain.Exceptions;
|
|
using adas_core.Domain.Models.MongoModels;
|
|
using adas_core.LdapLogin.Configuration;
|
|
using FluentValidation;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using MongoDB.Bson;
|
|
using System.DirectoryServices.Protocols;
|
|
using System.Net;
|
|
using Authorization = adas_core.Domain.Models.MongoModels.Authorization;
|
|
|
|
namespace adas_core.LdapLogin;
|
|
|
|
public class LdapLoginService : ILoginService
|
|
{
|
|
private readonly IAuthorityService _authorityService;
|
|
private readonly LdapConfig _ldapConfig;
|
|
private readonly ILogger<LdapLoginService> _logger;
|
|
private readonly Lazy<IUserService> _userService;
|
|
|
|
public LdapLoginService(
|
|
IOptions<LdapConfig> ldapConfig,
|
|
IValidator<LdapConfig> validator,
|
|
Lazy<IUserService> userService,
|
|
IAuthorityService authorityService,
|
|
ILogger<LdapLoginService> logger)
|
|
{
|
|
_userService = userService;
|
|
_authorityService = authorityService;
|
|
_logger = logger;
|
|
_ldapConfig = ldapConfig.Value;
|
|
validator.Validate(_ldapConfig, options => options.ThrowOnFailures());
|
|
}
|
|
|
|
public bool AllowPassword => true;
|
|
public UserEnum.LoginMethod Method => UserEnum.LoginMethod.Ldap;
|
|
|
|
public async Task<User> Login(string username, string password)
|
|
{
|
|
if (_ldapConfig.Server == null)
|
|
throw new LoginServicesException("LDAP Config not found");
|
|
|
|
var identifier = new LdapDirectoryIdentifier(_ldapConfig.Server, _ldapConfig.Port ?? 389);
|
|
var connection = new LdapConnection(identifier);
|
|
|
|
try
|
|
{
|
|
if (_ldapConfig.LdapUser != null)
|
|
{
|
|
_logger.LogInformation("[LDAP] Using configured LDAP user {LdapUser}", _ldapConfig.LdapUser);
|
|
connection.Credential = new NetworkCredential(_ldapConfig.LdapUser, _ldapConfig.LdapPassword);
|
|
}
|
|
else
|
|
{
|
|
var ldapUser = (!string.IsNullOrEmpty(_ldapConfig.UserDomainName)
|
|
? _ldapConfig.UserDomainName + @"\"
|
|
: "") + username;
|
|
|
|
connection.Credential = new NetworkCredential(ldapUser, password);
|
|
}
|
|
|
|
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;
|
|
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");
|
|
|
|
var userEntryLdap = GetUser(entry);
|
|
var user = await GetOrCreateUser(userEntryLdap, entry);
|
|
|
|
_logger.LogInformation("[LDAP] entry found and user {user}", user);
|
|
|
|
connection.Dispose();
|
|
|
|
return user ?? throw new LoginServicesException("LDAP User not found");
|
|
}
|
|
|
|
public Task<User> Login(HttpContext context)
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
public Task<User> Authenticate(string username, string password)
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
public Task<User?> GetById(ObjectId id)
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
public Task<User?> GetByEmail(string email)
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
public Task<User?> GetByUsername(string username)
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
public Task<List<User>> GetAllUsers()
|
|
=> throw new LoginServicesException("Not implemented");
|
|
|
|
private async Task<User?> GetOrCreateUser(User userEntryLdap, SearchResultEntry entry)
|
|
{
|
|
var userToReturn = (await _userService.Value.GetUserByUserName(userEntryLdap.UserName) ??
|
|
await _userService.Value.GetUserByName(userEntryLdap.Name)) ??
|
|
await _userService.Value.CreateUser(userEntryLdap);
|
|
|
|
if (userToReturn == null) return userToReturn;
|
|
|
|
userToReturn.Authorization = [];
|
|
|
|
var authorities = await CheckAuthorities(userToReturn, entry);
|
|
userToReturn.Authorization.AddRange(authorities);
|
|
|
|
return userToReturn;
|
|
}
|
|
|
|
private async Task<List<Authorization>> CheckAuthorities(User user, SearchResultEntry entry)
|
|
{
|
|
try
|
|
{
|
|
var authorizationMap = GetAuthoritiesMap(entry, user);
|
|
var authorizationWhiteList = GetAuthoritiesWhiteList(entry, user);
|
|
|
|
var whiteListDisplayIds = new HashSet<string?>(authorizationWhiteList.Select(a => a.DisplayId));
|
|
|
|
var uniqueMapAuthorizations = authorizationMap
|
|
.Where(a => !whiteListDisplayIds.Contains(a.DisplayId));
|
|
|
|
var combinedList = authorizationWhiteList.Concat(uniqueMapAuthorizations).ToList();
|
|
|
|
var userAuthorities = await _authorityService.GetUserAuthorities(user.Id);
|
|
|
|
foreach (var auth in combinedList)
|
|
{
|
|
var authFound = userAuthorities.Find(c => c.DisplayId == auth.DisplayId);
|
|
|
|
if (authFound is { CanUpdate: true })
|
|
{
|
|
authFound.Rol = auth.Rol;
|
|
await _authorityService.updateOne(authFound);
|
|
}
|
|
else
|
|
{
|
|
await _authorityService.InsertOne(auth);
|
|
}
|
|
}
|
|
|
|
return await _authorityService.GetUserAuthorities(user.Id);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError("[LDAP] CheckAuthorities error for user {user}: {error}",
|
|
user.UserName, e.Message);
|
|
|
|
return [];
|
|
}
|
|
}
|
|
|
|
private List<Authorization> GetAuthoritiesWhiteList(SearchResultEntry entry, User user)
|
|
{
|
|
try
|
|
{
|
|
var result = new List<Authorization>();
|
|
|
|
var whiteList = _ldapConfig.WhiteList.FindAll(u =>
|
|
(u.Name != null && entry.DistinguishedName.Contains(u.Name, StringComparison.CurrentCultureIgnoreCase)) ||
|
|
(u.Username != null &&
|
|
entry.Attributes[_ldapConfig.UserNameProperty]?[0]?.ToString()
|
|
?.Equals(u.Username, StringComparison.CurrentCultureIgnoreCase) == true)
|
|
);
|
|
|
|
foreach (var authorityMap in whiteList)
|
|
{
|
|
if (!Enum.TryParse<PermissionEnum.RolesType>(authorityMap.Rol, out _))
|
|
continue;
|
|
|
|
result.Add(new Authorization
|
|
{
|
|
UserId = user.Id,
|
|
DisplayId = authorityMap.DisplayId,
|
|
Rol = authorityMap.Rol
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError("[LDAP] GetAuthoritiesWhiteList error: {error}", e.Message);
|
|
return [];
|
|
}
|
|
}
|
|
|
|
private User GetUser(SearchResultEntry entry)
|
|
{
|
|
var user = new User
|
|
{
|
|
UserName = entry.Attributes[_ldapConfig.UserNameProperty]?[0]?.ToString() ?? ""
|
|
};
|
|
|
|
if (!string.IsNullOrWhiteSpace(_ldapConfig.FirstNameProperty))
|
|
{
|
|
var first = entry.Attributes[_ldapConfig.FirstNameProperty]?[0]?.ToString();
|
|
if (first != null)
|
|
user.Name = first;
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(_ldapConfig.LastNameProperty))
|
|
{
|
|
var last = entry.Attributes[_ldapConfig.LastNameProperty]?[0]?.ToString();
|
|
if (last != null)
|
|
user.Name = string.IsNullOrEmpty(user.Name)
|
|
? last
|
|
: $"{user.Name} {last}";
|
|
}
|
|
|
|
return user;
|
|
}
|
|
|
|
private List<Authorization> GetAuthoritiesMap(SearchResultEntry entry, User user)
|
|
{
|
|
try
|
|
{
|
|
var authorities = new List<Authorization>();
|
|
|
|
if (!_ldapConfig.AuthoritiesMap.Any())
|
|
return authorities;
|
|
|
|
var groupAttr = entry.Attributes[_ldapConfig.GroupsProperty];
|
|
|
|
if (groupAttr == null)
|
|
return authorities;
|
|
|
|
var groups = groupAttr.GetValues(typeof(string))
|
|
.Cast<string>()
|
|
.ToList();
|
|
|
|
foreach (var authorityMap in _ldapConfig.AuthoritiesMap)
|
|
{
|
|
if (groups.Exists(g =>
|
|
string.Equals(g, authorityMap.Group, StringComparison.CurrentCultureIgnoreCase)))
|
|
{
|
|
if (!Enum.TryParse<PermissionEnum.RolesType>(authorityMap.Rol, out _))
|
|
continue;
|
|
|
|
authorities.Add(new Authorization
|
|
{
|
|
UserId = user.Id,
|
|
DisplayId = authorityMap.DisplayId,
|
|
Rol = authorityMap.Rol
|
|
});
|
|
}
|
|
}
|
|
|
|
return authorities;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError("[LDAP] GetAuthoritiesMap error: {error}", e.Message);
|
|
return [];
|
|
}
|
|
}
|
|
}
|