using Berufsschule_HAM.Services; using Microsoft.AspNetCore.Mvc; using Novell.Directory.Ldap; using Berufsschule_HAM.Models; using Berufsschule_HAM.Helpers; using System.Security.Cryptography; using System.Text; using Microsoft.AspNetCore.Authorization; using System.Text.Json; using System.Buffers.Text; [Authorize(Roles = "CanManageUsers")] [Route("[controller]")] public class UsersController : Controller { private readonly LdapService _ldap; private readonly ILogger _logger; public UsersController(LdapService ldap, ILogger logger) { _ldap = ldap; _logger = logger; } [HttpGet("GetAll")] public async Task> GetAll(UsersGetAllRequestModel requestModel) { string? uid = requestModel.Uid; List attributes = ["cn", "sn", "title", "uid", "jpegPhoto", "userPassword", "description"]; if (!requestModel.Cn) attributes.Remove("cn"); if (!requestModel.Sn) attributes.Remove("sn"); if (!requestModel.Title) attributes.Remove("title"); if (!requestModel.JpegPhoto) attributes.Remove("jpegPhoto"); if (!requestModel.UserPassword) attributes.Remove("userPassword"); if (!requestModel.Description) attributes.Remove("description"); if (uid is null) { var users = await _ldap.ListUsersAsync([.. attributes]); return users; } else { var user = await _ldap.GetUserByUidAsync(uid, [.. attributes]); return [user]; } } [HttpDelete("Delete")] public async Task Delete(string uid) { try { var syncAssetOwnership = SynchronizationHelper.SyncAssetOwnership(_ldap, uid, ""); await _ldap.DeleteUserAsync(uid); await syncAssetOwnership; return new UsersDeleteRequestModel(true); } catch (Exception ex) { return new UsersDeleteRequestModel(false, ex.Message); } } [HttpPost("Create")] public async Task Create([FromBody] UsersCreateRequestModel requestModel) { if (requestModel is null) { return new() { Success = false, Exception = "The request model is null" }; } try { Task settingsTask = _ldap.GetAdminSettingsModelAsync(); string? jpegPhoto = requestModel.JpegPhoto; string? title = requestModel.Title; string userPassword = requestModel.UserPassword ?? ""; UserDescription? description = requestModel.Description; jpegPhoto ??= Convert.ToBase64String(System.IO.File.ReadAllBytes("wwwroot/user_default.jpeg")); // TODO: cleanup - make this a config setting string uid = UsersHelper.CreateUsername(requestModel.Cn ?? "", requestModel.Sn ?? ""); title ??= ""; description ??= new() {Address = new(), BirthDate = "", Workplace = "", Groups = []}; if (!userPassword.StartsWith('{')) { AdminSettingsModel settings = await settingsTask; byte[] passwordBytes = Encoding.UTF8.GetBytes(userPassword); byte[] hashedPassword = settings.hashAlgorithm?.ComputeHash(passwordBytes) ?? throw new Exception("Hash algorithm not instantiated yet"); userPassword = $"{{{settings.DefaultHashAlgorithm.ToUpperInvariant()}}}{Convert.ToBase64String(hashedPassword)}"; } LdapAttributeSet attributeSet = [ new LdapAttribute("objectClass", "inetOrgPerson"), new LdapAttribute("cn", requestModel.Cn), new LdapAttribute("sn", requestModel.Sn), new LdapAttribute("title", title), new LdapAttribute("uid", uid), new LdapAttribute("jpegPhoto", jpegPhoto), new LdapAttribute("description", JsonSerializer.Serialize(description)), new LdapAttribute("userPassword", userPassword), ]; await _ldap.CreateUser(uid, attributeSet); return new(){Success = true, Uid = uid}; } catch (Exception ex) { _logger.LogError("Unable to create user: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]); return new() {Success = false, Exception = ex.Message}; } } [HttpPost("Update")] public async Task Update([FromBody] UsersModifyRequestModel requestModel) { if (requestModel is null) { _logger.LogError("Unable to update a user because the UsersModifyRequestModel is null"); return new() { Success = false }; } try { Task settingsTask = _ldap.GetAdminSettingsModelAsync(); string uid = requestModel.Uid; UserModel? user = null; if (requestModel.NewUid is not null && requestModel.NewUid.Length > 0) { var syncAssetOwnership = SynchronizationHelper.SyncAssetOwnership(_ldap, uid, requestModel.NewUid); await _ldap.UpdateUser(uid, "uid", requestModel.NewUid); uid = requestModel.NewUid; await syncAssetOwnership; } if (requestModel.Title is not null) { await _ldap.UpdateUser(uid, "title", requestModel.Title); } if (requestModel.Description is not null) { await _ldap.UpdateUser(uid, "description", JsonSerializer.Serialize(requestModel.Description)); } if (requestModel.JpegPhoto is not null && requestModel.JpegPhoto.Length > 0) { await _ldap.UpdateUser(uid, "jpegPhoto", requestModel.JpegPhoto); } if (requestModel.UserPassword is not null && requestModel.UserPassword.Length > 0) { AdminSettingsModel settings = await settingsTask; byte[] passwordBytes = Encoding.UTF8.GetBytes(requestModel.UserPassword); byte[] hashedPassword = settings.hashAlgorithm?.ComputeHash(passwordBytes) ?? throw new Exception("Hash algorithm not instantiated yet"); requestModel.UserPassword = $"{{{settings.DefaultHashAlgorithm.ToUpperInvariant()}}}{Convert.ToBase64String(hashedPassword)}"; await _ldap.UpdateUser(uid, "userPassword", requestModel.UserPassword); } string newUid = uid; if (requestModel.Cn is not null) { await _ldap.UpdateUser(uid, "cn", requestModel.Cn); user ??= await _ldap.GetUserByUidAsync(uid); newUid = UsersHelper.CreateUsername(requestModel.Cn, user.Sn ?? ""); } if (requestModel.Sn is not null) { await _ldap.UpdateUser(uid, "sn", requestModel.Sn); user ??= await _ldap.GetUserByUidAsync(uid); newUid = UsersHelper.CreateUsername(user.Cn ?? "", requestModel.Sn); } if (newUid.Length == 0) { throw new Exception("Username cannot be empty"); } if (newUid != uid) { var syncAssetOwnership = SynchronizationHelper.SyncAssetOwnership(_ldap, uid, newUid); await _ldap.UpdateUser(uid, "uid", newUid); uid = newUid; await syncAssetOwnership; } return new() { Success = true, NewUid = uid }; } catch (Exception ex) { return new() { Success = false, Exception = ex.Message }; } } [HttpPost("AddGroup")] public async Task AddGroup([FromBody]UsersAddGroupRequestModel requestModel) { try { UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid); userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" }; userModel.Description.Groups ??= []; try { GroupModel group = await _ldap.GetGroupByCnAsync(requestModel.GroupUid, _ldap.GroupsAttributes); } catch (Exception) { return false; } userModel.Description.Groups.Add(requestModel.GroupUid); await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description)); return true; } catch (Exception ex) { _logger.LogError("Unable to add group {} to user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]); return false; } } [HttpPost("RemoveGroup")] public async Task RemoveGroup([FromBody]UsersRemoveGroupRequestModel requestModel) { try { UserModel userModel = await _ldap.GetUserByUidAsync(requestModel.Uid); userModel.Description ??= new() { Address = new(), BirthDate = "", Workplace = "" }; userModel.Description.Groups ??= []; userModel.Description.Groups.Remove(requestModel.GroupUid); await _ldap.UpdateUser(requestModel.Uid, "description", JsonSerializer.Serialize(userModel.Description)); return true; } catch (Exception ex) { _logger.LogError("Unable to remove group {} from user {}: {ex.Message} - {ex.StackTrace}", [requestModel.GroupUid, requestModel.Uid, ex.Message, ex.StackTrace]); return false; } } }