CRUD
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -15,6 +16,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,36 +1,165 @@
|
||||
using AceJobAgency.Data;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using AceJobAgency.Data;
|
||||
using AceJobAgency.Entities;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace AceJobAgency.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class UserController(DataContext context, IConfiguration configuration) : Controller
|
||||
public class UserController : Controller
|
||||
{
|
||||
[HttpPost]
|
||||
private readonly DataContext _context;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public UserController(DataContext context, IConfiguration configuration)
|
||||
{
|
||||
_context = context;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register(User user)
|
||||
{
|
||||
var userEmailExists = context.Users.Any(u => u.Email == user.Email);
|
||||
var userNationalRegistrationIdentityCardNumberExists = context.Users.Any(
|
||||
u => u.NationalRegistrationIdentityCardNumber
|
||||
== user.NationalRegistrationIdentityCardNumber
|
||||
);
|
||||
if (userEmailExists || userNationalRegistrationIdentityCardNumberExists)
|
||||
bool emailExists = _context.Users.Any(u => u.Email == user.Email);
|
||||
bool nricExists = _context.Users.Any(u =>
|
||||
u.NationalRegistrationIdentityCardNumber == user.NationalRegistrationIdentityCardNumber);
|
||||
if (emailExists || nricExists)
|
||||
{
|
||||
return BadRequest("User with the same email already exists.");
|
||||
return BadRequest("User with the same email or NRIC already exists.");
|
||||
}
|
||||
|
||||
var passwordHash = BCrypt.Net.BCrypt.HashPassword(user.Password);
|
||||
|
||||
user.Password = passwordHash;
|
||||
var userId = Guid.NewGuid().ToString();
|
||||
user.Id = userId;
|
||||
user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
|
||||
user.Id = Guid.NewGuid().ToString();
|
||||
user.IsActive = 1;
|
||||
|
||||
await context.Users.AddAsync(user);
|
||||
await context.SaveChangesAsync();
|
||||
await _context.Users.AddAsync(user);
|
||||
await _context.SaveChangesAsync();
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public IActionResult Login([FromBody] LoginRequest request)
|
||||
{
|
||||
var user = _context.Users.FirstOrDefault(u => u.Email == request.Email && u.IsActive == 1);
|
||||
if (user == null || !BCrypt.Net.BCrypt.Verify(request.Password, user.Password))
|
||||
{
|
||||
return Unauthorized("Invalid email or password.");
|
||||
}
|
||||
|
||||
var token = GenerateJwtToken(user);
|
||||
return Ok(new { token });
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("profile")]
|
||||
public IActionResult GetProfile()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = _context.Users.FirstOrDefault(u => u.Id == userId && u.IsActive == 1);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPut("profile")]
|
||||
public async Task<IActionResult> UpdateProfile(User updatedUser)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = _context.Users.FirstOrDefault(u => u.Id == userId && u.IsActive == 1);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
user.FirstName = updatedUser.FirstName;
|
||||
user.LastName = updatedUser.LastName;
|
||||
user.DateOfBirth = updatedUser.DateOfBirth;
|
||||
user.WhoAmI = updatedUser.WhoAmI;
|
||||
user.ResumeName = updatedUser.ResumeName;
|
||||
user.UpdatedAt = DateTime.Now;
|
||||
|
||||
_context.Users.Update(user);
|
||||
await _context.SaveChangesAsync();
|
||||
return Ok(user);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPut("change-password")]
|
||||
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordRequest request)
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = _context.Users.FirstOrDefault(u => u.Id == userId && u.IsActive == 1);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (!BCrypt.Net.BCrypt.Verify(request.CurrentPassword, user.Password))
|
||||
{
|
||||
return BadRequest("Current password is incorrect.");
|
||||
}
|
||||
|
||||
user.Password = BCrypt.Net.BCrypt.HashPassword(request.NewPassword);
|
||||
user.UpdatedAt = DateTime.Now;
|
||||
_context.Users.Update(user);
|
||||
await _context.SaveChangesAsync();
|
||||
return Ok("Password updated successfully.");
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpDelete("delete")]
|
||||
public async Task<IActionResult> DeleteAccount()
|
||||
{
|
||||
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||
var user = _context.Users.FirstOrDefault(u => u.Id == userId && u.IsActive == 1);
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
user.IsActive = 0;
|
||||
|
||||
_context.Users.Update(user);
|
||||
await _context.SaveChangesAsync();
|
||||
return Ok("Account deleted successfully.");
|
||||
}
|
||||
|
||||
private string GenerateJwtToken(User user)
|
||||
{
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_configuration["Authentication:Secret"] ?? "some_secret_key");
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity([
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||
new Claim(ClaimTypes.Email, user.Email)
|
||||
]),
|
||||
Expires = DateTime.UtcNow.AddHours(2),
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
|
||||
SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
return tokenHandler.WriteToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class ChangePasswordRequest
|
||||
{
|
||||
public string CurrentPassword { get; set; } = string.Empty;
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System.Text;
|
||||
using AceJobAgency.Data;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -7,9 +11,54 @@ var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddDbContext<DataContext>();
|
||||
|
||||
// Authentication
|
||||
var secret = builder.Configuration.GetValue<string>("Authentication:Secret");
|
||||
if (string.IsNullOrEmpty(secret))
|
||||
{
|
||||
throw new Exception("Secret is required for JWT authentication.");
|
||||
}
|
||||
builder.Services
|
||||
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
.AddJwtBearer(options =>
|
||||
{
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(
|
||||
Encoding.UTF8.GetBytes(secret)
|
||||
),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
var securityScheme = new OpenApiSecurityScheme
|
||||
{
|
||||
In = ParameterLocation.Header,
|
||||
Description = "Token",
|
||||
Name = "Authorization",
|
||||
Type = SecuritySchemeType.Http,
|
||||
BearerFormat = "JWT",
|
||||
Scheme = "Bearer",
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
};
|
||||
options.AddSecurityDefinition("Bearer", securityScheme);
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{ securityScheme, new List<string>() }
|
||||
});
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"DefaultConnection": "server=HOSTNAME;port=3306;database=DB_NAME;user=USERNAME;password=PASSWORD"
|
||||
},
|
||||
"Authentication": {
|
||||
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5",
|
||||
"TokenExpiresDays": 30
|
||||
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
|
||||
Reference in New Issue
Block a user