| | 1 | | using System.Text.RegularExpressions; |
| | 2 | | using MongoDB.Bson; |
| | 3 | | using MongoDB.Driver; |
| | 4 | | using WebApi.DAO; |
| | 5 | | using WebApi.Models; |
| | 6 | | using System.Data; |
| | 7 | |
|
| | 8 | |
|
| | 9 | | public class UserDAO : IUserDAO |
| | 10 | | { |
| | 11 | | private readonly IMongoCollection<User> _userMongoCollection; |
| | 12 | |
|
| 5 | 13 | | public UserDAO(MongoDbContext context) |
| 5 | 14 | | { |
| 5 | 15 | | _userMongoCollection = context.Database.GetCollection<User>("UserData"); |
| 5 | 16 | | } |
| | 17 | |
|
| | 18 | | public async Task<User> GetUserAsync(string username) |
| 2 | 19 | | { |
| | 20 | | try |
| 2 | 21 | | { |
| | 22 | | // Use a regular expression with case-insensitive search |
| 2 | 23 | | var filter = Builders<User>.Filter.Regex(u => u.Username, new BsonRegularExpression(username, "i")); |
| 2 | 24 | | var user = await _userMongoCollection.Find(filter).FirstOrDefaultAsync(); |
| | 25 | |
|
| 2 | 26 | | if (user == null) |
| 0 | 27 | | { |
| 0 | 28 | | throw new KeyNotFoundException($"User with username {username} not found."); |
| | 29 | | } |
| | 30 | |
|
| 2 | 31 | | return user; |
| | 32 | | } |
| 0 | 33 | | catch (Exception ex) |
| 0 | 34 | | { |
| 0 | 35 | | throw new Exception($"An error occurred when retrieving user: {ex.Message}", ex); |
| | 36 | | } |
| 2 | 37 | | } |
| | 38 | |
|
| | 39 | |
|
| | 40 | | public async Task RegisterUserAsync(User user) |
| 6 | 41 | | { |
| | 42 | | try |
| 6 | 43 | | { |
| 6 | 44 | | if (user == null) |
| 0 | 45 | | { |
| 0 | 46 | | throw new ArgumentNullException(nameof(user)); |
| | 47 | | } |
| | 48 | |
|
| | 49 | | // Check for duplicate data |
| 6 | 50 | | var filter = Builders<User>.Filter.Regex(u => u.Username, new BsonRegularExpression($"^{Regex.Escape(user.Us |
| 6 | 51 | | var duplicateData = await _userMongoCollection.Find(filter).FirstOrDefaultAsync(); |
| 6 | 52 | | if (duplicateData != null) |
| 1 | 53 | | { |
| 1 | 54 | | throw new Exception("Something went wrong."); // Vague response to make it harder for hackers |
| | 55 | | } |
| | 56 | |
|
| | 57 | | // Add data to the collection |
| 5 | 58 | | await _userMongoCollection.InsertOneAsync(user); |
| 5 | 59 | | } |
| 0 | 60 | | catch (DuplicateNameException) |
| 0 | 61 | | { |
| | 62 | | // Rethrow this exception directly without additional wrapping |
| 0 | 63 | | throw; |
| | 64 | | } |
| 1 | 65 | | catch (Exception ex) |
| 1 | 66 | | { |
| 1 | 67 | | throw new Exception("Failed to register user: " + ex.Message, ex); |
| | 68 | | } |
| 5 | 69 | | } |
| | 70 | |
|
| | 71 | |
|
| | 72 | | public async Task<User> ValidateUserAsync(string username, string password) |
| 2 | 73 | | { |
| | 74 | | try |
| 2 | 75 | | { |
| | 76 | | // Attempt to retrieve the user by username (case-insensitive) |
| 2 | 77 | | var filter = Builders<User>.Filter.Regex(u => u.Username, new BsonRegularExpression($"^{username}$", "i")); |
| 2 | 78 | | var user = await _userMongoCollection.Find(filter).FirstOrDefaultAsync(); |
| | 79 | |
|
| | 80 | | // If no user is found, or the password does not match, throw an exception |
| 2 | 81 | | if (user == null) |
| 0 | 82 | | { |
| 0 | 83 | | throw new UnauthorizedAccessException("Username is incorrect."); |
| | 84 | | } |
| | 85 | |
|
| 3 | 86 | | if (user.Password != password) { |
| 1 | 87 | | throw new UnauthorizedAccessException("Password is incorrect."); |
| | 88 | | } |
| | 89 | |
|
| | 90 | | // If user is found and password matches, return the user object |
| 1 | 91 | | return user; |
| | 92 | | } |
| 1 | 93 | | catch (UnauthorizedAccessException) |
| 1 | 94 | | { |
| | 95 | | // Re-throw specific exceptions to be handled or tested appropriately |
| 1 | 96 | | throw; |
| | 97 | | } |
| 0 | 98 | | catch (Exception ex) |
| 0 | 99 | | { |
| | 100 | | // Optionally, log and handle unexpected exceptions here, or wrap them if there's a good reason |
| 0 | 101 | | throw new Exception($"An unexpected error occurred when validating user: {ex.Message}", ex); |
| | 102 | | } |
| 1 | 103 | | } |
| | 104 | |
|
| | 105 | | public async Task<List<User>> GetAllUsersAsync() |
| 0 | 106 | | { |
| | 107 | | try |
| 0 | 108 | | { |
| | 109 | | // Define a sort by 'Role' in ascending order |
| 0 | 110 | | var sortByRole = Builders<User>.Sort.Ascending(u => u.Role); |
| | 111 | |
|
| | 112 | | // Retrieve all users from the collection and sort them by 'Role' |
| 0 | 113 | | return await _userMongoCollection.Find(new BsonDocument()).Sort(sortByRole).ToListAsync(); |
| | 114 | | } |
| 0 | 115 | | catch (Exception ex) |
| 0 | 116 | | { |
| | 117 | | // Handle or log the exception as needed |
| 0 | 118 | | throw new Exception($"Failed to retrieve all users sorted by role: {ex.Message}", ex); |
| | 119 | | } |
| | 120 | |
|
| 0 | 121 | | } |
| | 122 | |
|
| | 123 | | public async Task<User> UpdateUserAsync(User user) |
| 1 | 124 | | { |
| | 125 | | try |
| 1 | 126 | | { |
| | 127 | | // Create a filter to match the user by username (which remains unchanged) |
| 1 | 128 | | var filter = Builders<User>.Filter.Eq(userData => userData.Username, user.Username); |
| | 129 | |
|
| | 130 | | // Create an update definition to set new values for all updatable fields |
| 1 | 131 | | var updateDefinition = Builders<User>.Update |
| 1 | 132 | | .Set(userData => userData.Password, user.Password) |
| 1 | 133 | | .Set(userData => userData.Email, user.Email) |
| 1 | 134 | | .Set(userData => userData.Role, user.Role) |
| 1 | 135 | | .Set(userData => userData.Age, user.Age); // Add more fields as necessary |
| | 136 | |
|
| | 137 | | // Perform the update operation |
| 1 | 138 | | var result = await _userMongoCollection.UpdateOneAsync(filter, updateDefinition); |
| | 139 | |
|
| | 140 | | // Check if the update was successful, if not, handle it appropriately |
| 1 | 141 | | if (result.MatchedCount == 0) |
| 0 | 142 | | throw new KeyNotFoundException($"No user found with username {user.Username}"); |
| 1 | 143 | | if (result.ModifiedCount == 0) |
| 0 | 144 | | throw new Exception("No changes were made during the update operation."); |
| | 145 | |
|
| | 146 | | // Return the updated user data - consider fetching the user again if accurate data is needed |
| 1 | 147 | | return user; |
| | 148 | | } |
| 0 | 149 | | catch (Exception ex) |
| 0 | 150 | | { |
| 0 | 151 | | throw new Exception($"Failed to update user: {ex.Message}", ex); |
| | 152 | | } |
| 1 | 153 | | } |
| | 154 | |
|
| | 155 | |
|
| | 156 | | } |