UserController done
This commit is contained in:
parent
3423c9df34
commit
73bfd8d01c
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,4 +5,4 @@
|
||||
/backend/API/obj
|
||||
/backend/API/appsettings.Development.json
|
||||
/backend/API/appsettings.json
|
||||
/backend/API/bin/Debug/net8.0
|
||||
*bin
|
||||
|
@ -7,6 +7,17 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.15" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
236
backend/API/BusinessLogic/UserLogic.cs
Normal file
236
backend/API/BusinessLogic/UserLogic.cs
Normal file
@ -0,0 +1,236 @@
|
||||
using API.DBAccess;
|
||||
using API.Models.UserModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Org.BouncyCastle.Asn1.Ocsp;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace API.BusinessLogic
|
||||
{
|
||||
public class UserLogic
|
||||
{
|
||||
private readonly DbAccess _dbAccess;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public UserLogic(IConfiguration configuration, DbAccess dbAccess)
|
||||
{
|
||||
_dbAccess = dbAccess;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public async Task<IActionResult> GetUser(int userId)
|
||||
{
|
||||
User user = await _dbAccess.ReadUser(userId);
|
||||
|
||||
if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
|
||||
return new OkObjectResult(new { user.Id, user.UserName, user.Email });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> RegisterUser(CreateUserDTO userDTO)
|
||||
{
|
||||
if (!EmailCheck(userDTO.Email))
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Invalid email address" });
|
||||
}
|
||||
|
||||
if (!PasswordSecurity(userDTO.Password))
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Password is not up to the security standard" });
|
||||
}
|
||||
|
||||
var users = await _dbAccess.ReadAllUsers();
|
||||
|
||||
foreach (var item in users)
|
||||
{
|
||||
if (item.UserName == userDTO.UserName)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Username is already in use." });
|
||||
}
|
||||
|
||||
if (item.Email == userDTO.Email)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Email is already in use." });
|
||||
}
|
||||
}
|
||||
|
||||
string salt = Guid.NewGuid().ToString();
|
||||
string hashedPassword = ComputeHash(userDTO.Password, SHA256.Create(), salt);
|
||||
|
||||
User user = new User
|
||||
{
|
||||
UserName = userDTO.UserName,
|
||||
Email = userDTO.Email,
|
||||
Password = hashedPassword,
|
||||
Salt = salt,
|
||||
};
|
||||
|
||||
return await _dbAccess.CreateUser(user);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Login(LoginDTO loginDTO)
|
||||
{
|
||||
var user = await _dbAccess.ReadUserForLogin(loginDTO.EmailUsr);
|
||||
|
||||
if (user == null || user.Id == 0) { return new ConflictObjectResult(new { message = "Could not find user" }); }
|
||||
|
||||
string hashedPassword = ComputeHash(loginDTO.Password, SHA256.Create(), user.Salt);
|
||||
|
||||
if (user.Password == hashedPassword)
|
||||
{
|
||||
var token = GenerateJwtToken(user);
|
||||
|
||||
user = await UpdateRefreshToken(user);
|
||||
|
||||
return new OkObjectResult(new { token, user.UserName, user.Id, refreshToken = user.RefreshToken });
|
||||
}
|
||||
|
||||
return new ConflictObjectResult(new { message = "Invalid password" });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> EditProfile(UpdateUserDTO userDTO, int userId)
|
||||
{
|
||||
var profile = await _dbAccess.ReadUser(userId);
|
||||
var users = await _dbAccess.ReadAllUsers();
|
||||
|
||||
if (profile == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
|
||||
|
||||
if (!EmailCheck(userDTO.Email))
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Invalid email address" });
|
||||
}
|
||||
|
||||
foreach (var item in users)
|
||||
{
|
||||
if (item.UserName == userDTO.UserName)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Username is already in use." });
|
||||
}
|
||||
|
||||
if (item.Email == userDTO.Email)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Email is already in use." });
|
||||
}
|
||||
}
|
||||
|
||||
if (userDTO.Email == "" || userDTO.Email == null)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Please enter an email" });
|
||||
}
|
||||
|
||||
if (userDTO.UserName == "" || userDTO.UserName == null)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Please enter an userName" });
|
||||
}
|
||||
|
||||
profile.Email = userDTO.Email;
|
||||
profile.UserName = userDTO.UserName;
|
||||
|
||||
return await _dbAccess.UpdateUser(profile);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ChangePassword(ChangePasswordDTO passwordDTO, int userId)
|
||||
{
|
||||
var user = await _dbAccess.ReadUser(userId);
|
||||
|
||||
if (user == null) { return new ConflictObjectResult(new { message = "User does not exist" }); }
|
||||
|
||||
string hashedPassword = ComputeHash(passwordDTO.OldPassword, SHA256.Create(), user.Salt);
|
||||
|
||||
if (user.Password != hashedPassword)
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "Old password is incorrect" });
|
||||
|
||||
}
|
||||
|
||||
if (!PasswordSecurity(passwordDTO.NewPassword))
|
||||
{
|
||||
return new ConflictObjectResult(new { message = "New password is not up to the security standard" });
|
||||
}
|
||||
|
||||
string hashedNewPassword = ComputeHash(passwordDTO.NewPassword, SHA256.Create(), user.Salt);
|
||||
user.Password = hashedNewPassword;
|
||||
|
||||
return await _dbAccess.UpdatePassword(user);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> DeleteUser(int userId)
|
||||
{
|
||||
var user = await _dbAccess.ReadUserForDelete(userId);
|
||||
|
||||
if (user != null) { return await _dbAccess.DeleteUser(user); }
|
||||
|
||||
return new ConflictObjectResult(new { message = "Invalid user" });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> RefreshToken(string refreshToken)
|
||||
{
|
||||
User user = await _dbAccess.ReadUserByRefreshToken(refreshToken);
|
||||
if (user == null) { return new ConflictObjectResult(new { message = "Could not match refreshtoken" }); }
|
||||
user = await UpdateRefreshToken(user);
|
||||
string jwtToken = GenerateJwtToken(user);
|
||||
return new OkObjectResult(new { token = jwtToken, refreshToken = user.RefreshToken });
|
||||
}
|
||||
|
||||
private bool PasswordSecurity(string password)
|
||||
{
|
||||
var hasMinimum8Chars = new Regex(@".{8,}");
|
||||
|
||||
return hasMinimum8Chars.IsMatch(password);
|
||||
}
|
||||
|
||||
private bool EmailCheck(string email)
|
||||
{
|
||||
return new Regex(@".+@.+\..+").IsMatch(email);
|
||||
}
|
||||
|
||||
private static string ComputeHash(string input, HashAlgorithm algorithm, string salt)
|
||||
{
|
||||
Byte[] inputBytes = Encoding.UTF8.GetBytes(input);
|
||||
Byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
|
||||
|
||||
// Combine salt and input bytes
|
||||
Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length];
|
||||
saltBytes.CopyTo(saltedInput, 0);
|
||||
inputBytes.CopyTo(saltedInput, saltBytes.Length);
|
||||
|
||||
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput);
|
||||
|
||||
return BitConverter.ToString(hashedBytes);
|
||||
}
|
||||
|
||||
private string GenerateJwtToken(User user)
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(ClaimTypes.Name, user.UserName)
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes
|
||||
(_configuration["JwtSettings:Key"]));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
_configuration["JwtSettings:Issuer"],
|
||||
_configuration["JwtSettings:Audience"],
|
||||
claims,
|
||||
expires: DateTime.Now.AddHours(1),
|
||||
signingCredentials: creds);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
private async Task<User> UpdateRefreshToken(User user)
|
||||
{
|
||||
user.RefreshToken = Guid.NewGuid().ToString();
|
||||
user.RefreshTokenExpireAt = DateTime.Now.AddDays(30);
|
||||
await _dbAccess.UpdateUser(user);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
}
|
78
backend/API/Controllers/UserController.cs
Normal file
78
backend/API/Controllers/UserController.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using API.BusinessLogic;
|
||||
using API.Models.UserModels;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UserController : Controller
|
||||
{
|
||||
private readonly UserLogic _userLogic;
|
||||
|
||||
public UserController(UserLogic userLogic)
|
||||
{
|
||||
_userLogic = userLogic;
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpGet("get")]
|
||||
public async Task<IActionResult> ReadUser()
|
||||
{
|
||||
var claims = HttpContext.User.Claims;
|
||||
string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
|
||||
int userId = Convert.ToInt32(userIdString);
|
||||
return await _userLogic.GetUser(userId);
|
||||
}
|
||||
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginDTO loginDTO)
|
||||
{
|
||||
return await _userLogic.Login(loginDTO);
|
||||
}
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<IActionResult> CreateUser([FromBody] CreateUserDTO userDTO)
|
||||
{
|
||||
return await _userLogic.RegisterUser(userDTO);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPut("change-password")]
|
||||
public async Task<IActionResult> ChangePassword([FromBody] ChangePasswordDTO passwordDTO)
|
||||
{
|
||||
var claims = HttpContext.User.Claims;
|
||||
string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
|
||||
int userId = Convert.ToInt32(userIdString);
|
||||
return await _userLogic.ChangePassword(passwordDTO, userId);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpPut("update")]
|
||||
public async Task<IActionResult> UpdateUser([FromBody] UpdateUserDTO userDTO)
|
||||
{
|
||||
var claims = HttpContext.User.Claims;
|
||||
string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
|
||||
int userId = Convert.ToInt32(userIdString);
|
||||
return await _userLogic.EditProfile(userDTO, userId);
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
[HttpDelete("delete")]
|
||||
public async Task<IActionResult> DeleteUser()
|
||||
{
|
||||
var claims = HttpContext.User.Claims;
|
||||
string userIdString = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
|
||||
int userId = Convert.ToInt32(userIdString);
|
||||
return await _userLogic.DeleteUser(userId);
|
||||
}
|
||||
|
||||
[HttpPost("refreshtoken/{refreshToken}")]
|
||||
public async Task<IActionResult> RefreashToken(string refreshToken)
|
||||
{
|
||||
return await _userLogic.RefreshToken(refreshToken);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
12
backend/API/DBAccess/DBContext.cs
Normal file
12
backend/API/DBAccess/DBContext.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using API.Models.UserModels;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.DBAccess
|
||||
{
|
||||
public class DBContext : DbContext
|
||||
{
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
public DBContext(DbContextOptions<DBContext> options) : base(options) { }
|
||||
}
|
||||
}
|
93
backend/API/DBAccess/DbAccess.cs
Normal file
93
backend/API/DBAccess/DbAccess.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using API.Models.UserModels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.DBAccess
|
||||
{
|
||||
public class DbAccess
|
||||
{
|
||||
private readonly DBContext _context;
|
||||
|
||||
public DbAccess(DBContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<User> ReadUser(int userId)
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
}
|
||||
|
||||
public async Task<List<User>> ReadAllUsers()
|
||||
{
|
||||
return await _context.Users.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<User> ReadUserByRefreshToken(string refreshToken)
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);
|
||||
}
|
||||
|
||||
public async Task<User> ReadUserForDelete(int userId)
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
}
|
||||
|
||||
public async Task<User> ReadUserForLogin(string emailOrUsername)
|
||||
{
|
||||
if (emailOrUsername.Contains("@"))
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.Email == emailOrUsername);
|
||||
}
|
||||
else
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.UserName == emailOrUsername);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IActionResult> CreateUser(User user)
|
||||
{
|
||||
_context.Users.Add(user);
|
||||
|
||||
bool saved = await _context.SaveChangesAsync() == 1;
|
||||
|
||||
if (saved) { return new OkObjectResult(true); }
|
||||
|
||||
return new ConflictObjectResult(new { message = "Could not save to database" });
|
||||
|
||||
}
|
||||
|
||||
public async Task<IActionResult> UpdateUser(User user)
|
||||
{
|
||||
_context.Entry(user).State = EntityState.Modified;
|
||||
|
||||
bool saved = await _context.SaveChangesAsync() == 1;
|
||||
|
||||
if (saved) { return new OkObjectResult(user); }
|
||||
|
||||
return new ConflictObjectResult(new { message = "Could not save to database" });
|
||||
|
||||
}
|
||||
|
||||
public async Task<IActionResult> UpdatePassword(User user)
|
||||
{
|
||||
_context.Entry(user).State = EntityState.Modified;
|
||||
|
||||
bool saved = await _context.SaveChangesAsync() == 1;
|
||||
|
||||
if (saved) { return new OkObjectResult(user); }
|
||||
|
||||
return new ConflictObjectResult(new { message = "Could not save to database" });
|
||||
}
|
||||
|
||||
public async Task<IActionResult> DeleteUser(User user)
|
||||
{
|
||||
_context.Users.Remove(user);
|
||||
bool saved = await _context.SaveChangesAsync() >= 0;
|
||||
|
||||
if (saved) { return new OkObjectResult(saved); }
|
||||
|
||||
return new ConflictObjectResult(new { message = "Could not save to database" });
|
||||
}
|
||||
}
|
||||
}
|
59
backend/API/Migrations/20250423074254_init.Designer.cs
generated
Normal file
59
backend/API/Migrations/20250423074254_init.Designer.cs
generated
Normal file
@ -0,0 +1,59 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using API.DBAccess;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
[DbContext(typeof(DBContext))]
|
||||
[Migration("20250423074254_init")]
|
||||
partial class init
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("API.Models.UserModels.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("RefreshToken")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("RefreshTokenExpireAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
45
backend/API/Migrations/20250423074254_init.cs
Normal file
45
backend/API/Migrations/20250423074254_init.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using MySql.EntityFrameworkCore.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class init : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterDatabase()
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||
UserName = table.Column<string>(type: "longtext", nullable: false),
|
||||
Password = table.Column<string>(type: "longtext", nullable: false),
|
||||
Email = table.Column<string>(type: "longtext", nullable: false),
|
||||
Salt = table.Column<string>(type: "longtext", nullable: true),
|
||||
RefreshToken = table.Column<string>(type: "longtext", nullable: true),
|
||||
RefreshTokenExpireAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
56
backend/API/Migrations/DBContextModelSnapshot.cs
Normal file
56
backend/API/Migrations/DBContextModelSnapshot.cs
Normal file
@ -0,0 +1,56 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using API.DBAccess;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Migrations
|
||||
{
|
||||
[DbContext(typeof(DBContext))]
|
||||
partial class DBContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "8.0.10")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
modelBuilder.Entity("API.Models.UserModels.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("RefreshToken")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<DateTime>("RefreshTokenExpireAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
9
backend/API/Models/UserModels/ChangePasswordDTO.cs
Normal file
9
backend/API/Models/UserModels/ChangePasswordDTO.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace API.Models.UserModels
|
||||
{
|
||||
public class ChangePasswordDTO
|
||||
{
|
||||
public string OldPassword { get; set; }
|
||||
|
||||
public string NewPassword { get; set; }
|
||||
}
|
||||
}
|
11
backend/API/Models/UserModels/CreateUserDTO.cs
Normal file
11
backend/API/Models/UserModels/CreateUserDTO.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace API.Models.UserModels
|
||||
{
|
||||
public class CreateUserDTO
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
9
backend/API/Models/UserModels/LoginDTO.cs
Normal file
9
backend/API/Models/UserModels/LoginDTO.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace API.Models.UserModels
|
||||
{
|
||||
public class LoginDTO
|
||||
{
|
||||
public string EmailUsr { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
9
backend/API/Models/UserModels/UpdateUserDTO.cs
Normal file
9
backend/API/Models/UserModels/UpdateUserDTO.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace API.Models.UserModels
|
||||
{
|
||||
public class UpdateUserDTO
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
19
backend/API/Models/UserModels/User.cs
Normal file
19
backend/API/Models/UserModels/User.cs
Normal file
@ -0,0 +1,19 @@
|
||||
namespace API.Models.UserModels
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
public string Email { get; set; }
|
||||
|
||||
public string? Salt { get; set; }
|
||||
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
public DateTime RefreshTokenExpireAt { get; set; }
|
||||
}
|
||||
}
|
@ -1,25 +1,34 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
using API;
|
||||
using Microsoft.AspNetCore;
|
||||
using API.DBAccess;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
internal class Program
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
private static void Main(string[] args)
|
||||
{
|
||||
var app = CreateWebHostBuilder(args).Build();
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
RunMigrations(app);
|
||||
|
||||
app.UseAuthorization();
|
||||
app.Run();
|
||||
}
|
||||
|
||||
app.MapControllers();
|
||||
// Calls the startup class and creates the webinterface
|
||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
||||
WebHost.CreateDefaultBuilder(args)
|
||||
.UseUrls("http://0.0.0.0:5000")
|
||||
.UseStartup<Startup>();
|
||||
|
||||
app.Run();
|
||||
public static async void RunMigrations(IWebHost app)
|
||||
{
|
||||
await using var scope = app.Services.CreateAsyncScope();
|
||||
await using var db = scope.ServiceProvider.GetService<DBContext>();
|
||||
|
||||
if (db != null)
|
||||
{
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
}
|
||||
}
|
122
backend/API/Startup.cs
Normal file
122
backend/API/Startup.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using System.Text;
|
||||
using API.DBAccess;
|
||||
using API.BusinessLogic;
|
||||
|
||||
namespace API
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public IConfiguration _configuration { get; }
|
||||
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Sets the connectionstring to the database so dbcontext can find it
|
||||
services.AddDbContext<DBContext>(options =>
|
||||
options.UseMySQL(_configuration.GetConnectionString("Database")));
|
||||
|
||||
services.AddScoped<DbAccess>();
|
||||
services.AddScoped<UserLogic>();
|
||||
|
||||
services.AddControllers();
|
||||
|
||||
services.AddAuthentication(x =>
|
||||
{
|
||||
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
}).AddJwtBearer(x =>
|
||||
{
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidIssuer = _configuration["JwtSettings:Issuer"],
|
||||
ValidAudience = _configuration["JwtSettings:Audience"],
|
||||
IssuerSigningKey = new SymmetricSecurityKey
|
||||
(
|
||||
Encoding.UTF8.GetBytes(_configuration["JwtSettings:Key"])
|
||||
),
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll", builder =>
|
||||
{
|
||||
builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod().AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
services.AddSwaggerGen(c =>
|
||||
{
|
||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
|
||||
|
||||
// Configure Swagger to use Bearer token authentication
|
||||
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
Description = "JWT Authorization header using the Bearer scheme",
|
||||
Type = SecuritySchemeType.Http,
|
||||
Scheme = "bearer"
|
||||
});
|
||||
|
||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
{
|
||||
{
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
},
|
||||
new string[] { }
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(c =>
|
||||
{
|
||||
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
|
||||
});
|
||||
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseCors("AllowAll");
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
namespace API
|
||||
{
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateOnly Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user