diff --git a/AceJobAgency/Controllers/UserController.cs b/AceJobAgency/Controllers/UserController.cs
index fef9a20..b80e234 100644
--- a/AceJobAgency/Controllers/UserController.cs
+++ b/AceJobAgency/Controllers/UserController.cs
@@ -1,7 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
-using System.Text.RegularExpressions;
using AceJobAgency.Data;
using AceJobAgency.Entities;
using AceJobAgency.Utilities;
@@ -17,11 +16,13 @@ namespace AceJobAgency.Controllers
{
private readonly DataContext _context;
private readonly IConfiguration _configuration;
+ private readonly IEncryptionService _encryptionService;
- public UserController(DataContext context, IConfiguration configuration)
+ public UserController(DataContext context, IConfiguration configuration, IEncryptionService encryptionService)
{
_context = context;
_configuration = configuration;
+ _encryptionService = encryptionService;
}
[HttpPost("register")]
@@ -32,9 +33,10 @@ namespace AceJobAgency.Controllers
return BadRequest("Password must be at least 12 characters long and include uppercase, lowercase, number, and special character.");
}
+ var encryptedNric = _encryptionService.Encrypt(user.NationalRegistrationIdentityCardNumber);
+
var emailExists = _context.Users.Any(u => u.Email == user.Email);
- var nricExists = _context.Users.Any(u =>
- u.NationalRegistrationIdentityCardNumber == user.NationalRegistrationIdentityCardNumber);
+ var nricExists = _context.Users.Any(u => u.NationalRegistrationIdentityCardNumber == encryptedNric);
if (emailExists || nricExists)
{
return BadRequest("User with the same email or NRIC already exists.");
@@ -43,10 +45,23 @@ namespace AceJobAgency.Controllers
user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
user.Id = Guid.NewGuid().ToString();
user.IsActive = 1;
+ user.NationalRegistrationIdentityCardNumber = encryptedNric;
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
- return Ok(user);
+
+ var response = new
+ {
+ user.Id,
+ user.Email,
+ NationalRegistrationIdentityCardNumber = _encryptionService.Decrypt(user.NationalRegistrationIdentityCardNumber),
+ user.FirstName,
+ user.LastName,
+ user.DateOfBirth,
+ user.WhoAmI,
+ user.ResumeName,
+ };
+ return Ok(response);
}
[HttpPost("login")]
@@ -72,7 +87,20 @@ namespace AceJobAgency.Controllers
{
return NotFound();
}
- return Ok(user);
+
+ var decryptedNric = _encryptionService.Decrypt(user.NationalRegistrationIdentityCardNumber);
+ var response = new
+ {
+ user.Id,
+ user.Email,
+ NationalRegistrationIdentityCardNumber = decryptedNric,
+ user.FirstName,
+ user.LastName,
+ user.DateOfBirth,
+ user.WhoAmI,
+ user.ResumeName,
+ };
+ return Ok(response);
}
[Authorize]
@@ -174,4 +202,4 @@ namespace AceJobAgency.Controllers
public string CurrentPassword { get; set; } = string.Empty;
public string NewPassword { get; set; } = string.Empty;
}
-}
+}
\ No newline at end of file
diff --git a/AceJobAgency/Entities/User.cs b/AceJobAgency/Entities/User.cs
index 8228ddc..3db6b60 100644
--- a/AceJobAgency/Entities/User.cs
+++ b/AceJobAgency/Entities/User.cs
@@ -20,7 +20,7 @@ namespace AceJobAgency.Entities
public required int Gender { get; set; }
[Required]
- [StringLength(9, MinimumLength = 9)]
+ [StringLength(255)]
public required string NationalRegistrationIdentityCardNumber { get; set; }
[Required]
diff --git a/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.Designer.cs b/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.Designer.cs
new file mode 100644
index 0000000..a55ad6e
--- /dev/null
+++ b/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.Designer.cs
@@ -0,0 +1,91 @@
+//
+using System;
+using AceJobAgency.Data;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace AceJobAgency.Migrations
+{
+ [DbContext(typeof(DataContext))]
+ [Migration("20250207154242_UpdatedNRIC")]
+ partial class UpdatedNRIC
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "8.0.2")
+ .HasAnnotation("Relational:MaxIdentifierLength", 64);
+
+ MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
+
+ modelBuilder.Entity("AceJobAgency.Entities.User", b =>
+ {
+ b.Property("Id")
+ .HasMaxLength(36)
+ .HasColumnType("varchar(36)");
+
+ b.Property("CreatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("DateOfBirth")
+ .HasColumnType("datetime(6)");
+
+ b.Property("Email")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("varchar(128)");
+
+ b.Property("FirstName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("Gender")
+ .HasColumnType("int");
+
+ b.Property("IsActive")
+ .HasColumnType("int");
+
+ b.Property("LastName")
+ .IsRequired()
+ .HasMaxLength(50)
+ .HasColumnType("varchar(50)");
+
+ b.Property("NationalRegistrationIdentityCardNumber")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("varchar(255)");
+
+ b.Property("Password")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("varchar(128)");
+
+ b.Property("ResumeName")
+ .IsRequired()
+ .HasMaxLength(128)
+ .HasColumnType("varchar(128)");
+
+ b.Property("UpdatedAt")
+ .HasColumnType("datetime(6)");
+
+ b.Property("WhoAmI")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("varchar(255)");
+
+ b.HasKey("Id");
+
+ b.ToTable("Users");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.cs b/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.cs
new file mode 100644
index 0000000..789728d
--- /dev/null
+++ b/AceJobAgency/Migrations/20250207154242_UpdatedNRIC.cs
@@ -0,0 +1,42 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace AceJobAgency.Migrations
+{
+ ///
+ public partial class UpdatedNRIC : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "NationalRegistrationIdentityCardNumber",
+ table: "Users",
+ type: "varchar(255)",
+ maxLength: 255,
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "varchar(9)",
+ oldMaxLength: 9)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ .OldAnnotation("MySql:CharSet", "utf8mb4");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "NationalRegistrationIdentityCardNumber",
+ table: "Users",
+ type: "varchar(9)",
+ maxLength: 9,
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "varchar(255)",
+ oldMaxLength: 255)
+ .Annotation("MySql:CharSet", "utf8mb4")
+ .OldAnnotation("MySql:CharSet", "utf8mb4");
+ }
+ }
+}
diff --git a/AceJobAgency/Migrations/DataContextModelSnapshot.cs b/AceJobAgency/Migrations/DataContextModelSnapshot.cs
index 963de11..f7868ce 100644
--- a/AceJobAgency/Migrations/DataContextModelSnapshot.cs
+++ b/AceJobAgency/Migrations/DataContextModelSnapshot.cs
@@ -57,8 +57,8 @@ namespace AceJobAgency.Migrations
b.Property("NationalRegistrationIdentityCardNumber")
.IsRequired()
- .HasMaxLength(9)
- .HasColumnType("varchar(9)");
+ .HasMaxLength(255)
+ .HasColumnType("varchar(255)");
b.Property("Password")
.IsRequired()
diff --git a/AceJobAgency/Program.cs b/AceJobAgency/Program.cs
index bbc458c..ee744a1 100644
--- a/AceJobAgency/Program.cs
+++ b/AceJobAgency/Program.cs
@@ -1,5 +1,6 @@
using System.Text;
using AceJobAgency.Data;
+using AceJobAgency.Utilities;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
@@ -60,6 +61,8 @@ builder.Services.AddSwaggerGen(options =>
});
});
+builder.Services.AddScoped();
+
var app = builder.Build();
// Configure the HTTP request pipeline.
diff --git a/AceJobAgency/Utilities/Cryptography.cs b/AceJobAgency/Utilities/Cryptography.cs
new file mode 100644
index 0000000..293873c
--- /dev/null
+++ b/AceJobAgency/Utilities/Cryptography.cs
@@ -0,0 +1,61 @@
+using System.Security.Cryptography;
+
+namespace AceJobAgency.Utilities;
+
+public interface IEncryptionService
+{
+ string Encrypt(string plainText);
+ string Decrypt(string cipherText);
+}
+
+public class AesEncryptionService : IEncryptionService
+{
+ private readonly byte[] _key;
+
+ public AesEncryptionService(IConfiguration configuration)
+ {
+ var encryptionKey = configuration["Encryption:Key"];
+ if (string.IsNullOrEmpty(encryptionKey))
+ throw new ArgumentException("Encryption key is not configured.");
+ _key = Convert.FromBase64String(encryptionKey);
+ }
+
+ public string Encrypt(string plainText)
+ {
+ using var aes = Aes.Create();
+ aes.Key = _key;
+ aes.GenerateIV();
+
+ using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
+ using var ms = new MemoryStream();
+ using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
+ using (var sw = new StreamWriter(cs))
+ sw.Write(plainText);
+
+ var encryptedBytes = ms.ToArray();
+ var iv = aes.IV;
+ var combined = new byte[iv.Length + encryptedBytes.Length];
+ Buffer.BlockCopy(iv, 0, combined, 0, iv.Length);
+ Buffer.BlockCopy(encryptedBytes, 0, combined, iv.Length, encryptedBytes.Length);
+ return Convert.ToBase64String(combined);
+ }
+
+ public string Decrypt(string cipherText)
+ {
+ var combined = Convert.FromBase64String(cipherText);
+ var iv = new byte[16];
+ var encryptedBytes = new byte[combined.Length - iv.Length];
+ Buffer.BlockCopy(combined, 0, iv, 0, iv.Length);
+ Buffer.BlockCopy(combined, iv.Length, encryptedBytes, 0, encryptedBytes.Length);
+
+ using var aes = Aes.Create();
+ aes.Key = _key;
+ aes.IV = iv;
+
+ using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
+ using var ms = new MemoryStream(encryptedBytes);
+ using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
+ using var sr = new StreamReader(cs);
+ return sr.ReadToEnd();
+ }
+}
\ No newline at end of file
diff --git a/AceJobAgency/appsettings.json b/AceJobAgency/appsettings.json
index 3b1ae9d..04b889e 100644
--- a/AceJobAgency/appsettings.json
+++ b/AceJobAgency/appsettings.json
@@ -5,6 +5,9 @@
"Authentication": {
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
},
+ "Encryption": {
+ "Key": "laVspGq/dNFECeUP93fUF05T422cLjieKPotTI8Wgfc="
+ },
"Logging": {
"LogLevel": {
"Default": "Information",