Compare commits

..

No commits in common. "3ffd8d23a0d7fbf433cafc0e5249f8c7aa21c232" and "5201e6e4748e965cc9f6f55f058800f990bf2c20" have entirely different histories.

11 changed files with 31 additions and 338 deletions

View File

@ -10,7 +10,6 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -15,7 +15,7 @@ namespace Api.Controllers
_context = context; _context = context;
} }
// For at få json webtokens til at virke skriv [Authorize] over de endpoints
[HttpGet] [HttpGet]
public async Task<IActionResult> GetDevices(int userId) public async Task<IActionResult> GetDevices(int userId)
{ {

View File

@ -1,10 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Api.Models; using Api.Models;
using Api.DBAccess; using Api.DBAccess;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Api.Controllers namespace Api.Controllers
{ {
@ -13,12 +9,10 @@ namespace Api.Controllers
public class UserController : Controller public class UserController : Controller
{ {
private readonly DBContext _context; private readonly DBContext _context;
private readonly IConfiguration _configuration;
public UserController(DBContext context, IConfiguration configuration) public UserController(DBContext context)
{ {
_context = context; _context = context;
_configuration = configuration;
} }
[HttpPost("Login")] [HttpPost("Login")]
@ -26,9 +20,8 @@ namespace Api.Controllers
{ {
DbAccess dBAccess = new DbAccess(_context); DbAccess dBAccess = new DbAccess(_context);
user = await dBAccess.Login(user); user = await dBAccess.Login(user);
if (user.Id == 0) { return Unauthorized(new { error = "Invalid username or password" }); } if (user.Id == 0) { return BadRequest(new { error = "User can't be logged in" }); }
var token = GenerateJwtToken(user); return Ok(user);
return Ok(new { token, user.UserName, user.Id });
} }
[HttpPost("Create")] [HttpPost("Create")]
@ -48,37 +41,5 @@ namespace Api.Controllers
if (!success) { return BadRequest(new { error = "User can't be edited" }); } if (!success) { return BadRequest(new { error = "User can't be edited" }); }
return Ok(); return Ok();
} }
[HttpDelete("Delete/{userId}")]
public async Task<IActionResult> DeleteUser(int userId)
{
DbAccess dbAccess = new DbAccess(_context);
bool success = await dbAccess.DeleteUser(userId);
if (!success) { return BadRequest(new { error = "User can't be deleted" }); }
return Ok();
}
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.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
}
} }
} }

View File

@ -1,8 +1,5 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Api.Models; using Api.Models;
using System.Text;
using System.Runtime.Intrinsics.Arm;
using System.Security.Cryptography;
namespace Api.DBAccess namespace Api.DBAccess
@ -31,11 +28,6 @@ namespace Api.DBAccess
{ {
user.Devices = new List<Device>(); user.Devices = new List<Device>();
} }
string salt = Guid.NewGuid().ToString();
string hashedPassword = ComputeHash(user.Password, SHA256.Create(), salt);
user.Salt = salt;
user.Password = hashedPassword;
_context.Users.Add(user); _context.Users.Add(user);
return await _context.SaveChangesAsync() == 1; return await _context.SaveChangesAsync() == 1;
@ -44,11 +36,10 @@ namespace Api.DBAccess
public async Task<User> Login(User user) public async Task<User> Login(User user)
{ {
var profile = await _context.Users.FirstAsync(u => u.UserName == user.UserName); var profile = await _context.Users.FirstAsync(u => u.UserName == user.UserName);
string hashedPassword = ComputeHash(user.Password, SHA256.Create(), profile.Salt);
if (hashedPassword == user.Password) if (profile.Password == user.Password)
{ {
profile.Password = "";
return profile; return profile;
} }
return new User(); return new User();
@ -67,25 +58,6 @@ namespace Api.DBAccess
return await _context.SaveChangesAsync() == 1; return await _context.SaveChangesAsync() == 1;
} }
public async Task<bool> DeleteUser(int userId)
{
var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId);
if (user != null)
{
if (user.Devices != null && user.Devices.Count > 0)
{
foreach (var item in user.Devices)
{
var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == item.Id);
if (device != null) { _context.Devices.Remove(device); }
}
}
_context.Users.Remove(user);
return await _context.SaveChangesAsync() == 1;
}
return false;
}
public async Task<List<Device>> ReadDevices(int userId) public async Task<List<Device>> ReadDevices(int userId)
{ {
var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId); var user = await _context.Users.Include(u => u.Devices).FirstOrDefaultAsync(u => u.Id == userId);
@ -110,21 +82,6 @@ namespace Api.DBAccess
return await _context.SaveChangesAsync() == 1; return await _context.SaveChangesAsync() == 1;
} }
public async Task<bool> UpdateDevice(Device device, int deviceId)
{
var device1 = await _context.Devices.FirstAsync(u => u.Id == deviceId);
device1.TempLow = device.TempLow;
device1.TempHigh = device.TempHigh;
device1.ReferenceId = device.ReferenceId;
device1.Name = device.Name;
return await _context.SaveChangesAsync() == 1;
}
public async Task<List<TemperatureLogs>> ReadLogs(int deviceId) public async Task<List<TemperatureLogs>> ReadLogs(int deviceId)
{ {
var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == deviceId); var device = await _context.Devices.Include(d => d.Logs).FirstOrDefaultAsync(d => d.Id == deviceId);
@ -136,19 +93,17 @@ namespace Api.DBAccess
return logs; return logs;
} }
private static string ComputeHash(string input, HashAlgorithm algorithm, string salt) public async Task<bool> UpdateDevice(Device device, int deviceId)
{ {
Byte[] inputBytes = Encoding.UTF8.GetBytes(input); var device1 = await _context.Devices.FirstAsync(u => u.Id == deviceId);
Byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
// Combine salt and input bytes device1.TempLow = device.TempLow;
Byte[] saltedInput = new Byte[saltBytes.Length + inputBytes.Length];
saltBytes.CopyTo(saltedInput, 0);
inputBytes.CopyTo(saltedInput, saltBytes.Length);
Byte[] hashedBytes = algorithm.ComputeHash(saltedInput); device1.TempHigh = device.TempHigh;
return BitConverter.ToString(hashedBytes); device1.ReferenceId = device.ReferenceId;
return await _context.SaveChangesAsync() == 1;
} }
} }
} }

View File

@ -1,132 +0,0 @@
// <auto-generated />
using System;
using Api;
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("20250319124149_AddedSaltUserNameDevice")]
partial class AddedSaltUserNameDevice
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.3");
modelBuilder.Entity("Api.Models.Device", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ReferenceId")
.HasColumnType("TEXT");
b.Property<double>("TempHigh")
.HasColumnType("REAL");
b.Property<double>("TempLow")
.HasColumnType("REAL");
b.Property<int?>("UserId")
.HasColumnType("INTEGER");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("Devices");
});
modelBuilder.Entity("Api.Models.TemperatureLogs", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<DateTime>("Date")
.HasColumnType("TEXT");
b.Property<int?>("DeviceId")
.HasColumnType("INTEGER");
b.Property<double>("TempHigh")
.HasColumnType("REAL");
b.Property<double>("TempLow")
.HasColumnType("REAL");
b.Property<double>("Temperature")
.HasColumnType("REAL");
b.HasKey("Id");
b.HasIndex("DeviceId");
b.ToTable("TemperatureLogs");
});
modelBuilder.Entity("Api.Models.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("Email")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("Salt")
.HasColumnType("TEXT");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("Api.Models.Device", b =>
{
b.HasOne("Api.Models.User", null)
.WithMany("Devices")
.HasForeignKey("UserId");
});
modelBuilder.Entity("Api.Models.TemperatureLogs", b =>
{
b.HasOne("Api.Models.Device", null)
.WithMany("Logs")
.HasForeignKey("DeviceId");
});
modelBuilder.Entity("Api.Models.Device", b =>
{
b.Navigation("Logs");
});
modelBuilder.Entity("Api.Models.User", b =>
{
b.Navigation("Devices");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,39 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Api.Migrations
{
/// <inheritdoc />
public partial class AddedSaltUserNameDevice : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Salt",
table: "Users",
type: "TEXT",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "Name",
table: "Devices",
type: "TEXT",
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Salt",
table: "Users");
migrationBuilder.DropColumn(
name: "Name",
table: "Devices");
}
}
}

View File

@ -17,16 +17,12 @@ namespace Api.Migrations
#pragma warning disable 612, 618 #pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.3"); modelBuilder.HasAnnotation("ProductVersion", "9.0.3");
modelBuilder.Entity("Api.Models.Device", b => modelBuilder.Entity("Models.Device", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
.HasColumnType("INTEGER"); .HasColumnType("INTEGER");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.Property<string>("ReferenceId") b.Property<string>("ReferenceId")
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -46,7 +42,7 @@ namespace Api.Migrations
b.ToTable("Devices"); b.ToTable("Devices");
}); });
modelBuilder.Entity("Api.Models.TemperatureLogs", b => modelBuilder.Entity("Models.TemperatureLogs", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -74,7 +70,7 @@ namespace Api.Migrations
b.ToTable("TemperatureLogs"); b.ToTable("TemperatureLogs");
}); });
modelBuilder.Entity("Api.Models.User", b => modelBuilder.Entity("Models.User", b =>
{ {
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd() .ValueGeneratedOnAdd()
@ -88,9 +84,6 @@ namespace Api.Migrations
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
b.Property<string>("Salt")
.HasColumnType("TEXT");
b.Property<string>("UserName") b.Property<string>("UserName")
.IsRequired() .IsRequired()
.HasColumnType("TEXT"); .HasColumnType("TEXT");
@ -100,26 +93,26 @@ namespace Api.Migrations
b.ToTable("Users"); b.ToTable("Users");
}); });
modelBuilder.Entity("Api.Models.Device", b => modelBuilder.Entity("Models.Device", b =>
{ {
b.HasOne("Api.Models.User", null) b.HasOne("Models.User", null)
.WithMany("Devices") .WithMany("Devices")
.HasForeignKey("UserId"); .HasForeignKey("UserId");
}); });
modelBuilder.Entity("Api.Models.TemperatureLogs", b => modelBuilder.Entity("Models.TemperatureLogs", b =>
{ {
b.HasOne("Api.Models.Device", null) b.HasOne("Models.Device", null)
.WithMany("Logs") .WithMany("Logs")
.HasForeignKey("DeviceId"); .HasForeignKey("DeviceId");
}); });
modelBuilder.Entity("Api.Models.Device", b => modelBuilder.Entity("Models.Device", b =>
{ {
b.Navigation("Logs"); b.Navigation("Logs");
}); });
modelBuilder.Entity("Api.Models.User", b => modelBuilder.Entity("Models.User", b =>
{ {
b.Navigation("Devices"); b.Navigation("Devices");
}); });

View File

@ -4,8 +4,6 @@
{ {
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; }
public double TempHigh { get; set; } public double TempHigh { get; set; }
public double TempLow { get; set; } public double TempLow { get; set; }

View File

@ -10,8 +10,6 @@
public string Email { get; set; } public string Email { get; set; }
public string? Salt { get; set; }
public List<Device>? Devices { get; set; } public List<Device>? Devices { get; set; }
} }
} }

View File

@ -2,28 +2,14 @@ using Api;
using Microsoft.AspNetCore; using Microsoft.AspNetCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
class Program var app = WebHost.CreateDefaultBuilder(args)
{ .UseUrls("http://0.0.0.0:5000")
public static void Main(string[] args) .UseStartup<Startup>()
{ .Build();
var app = CreateWebHostBuilder(args).Build();
RunMigrations(app); await using var scope = app.Services.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetService<DBContext>();
app.Run(); await db.Database.MigrateAsync();
}
// Calls the startup class and creates the webinterface
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("0.0.0.0:5000")
.UseStartup<Startup>();
public static async void RunMigrations(IWebHost app)
{
await using var scope = app.Services.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetService<DbContext>();
await db.Database.MigrateAsync();
}
}
app.Run();

View File

@ -1,8 +1,5 @@
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using System.Text;
namespace Api namespace Api
{ {
@ -25,29 +22,6 @@ namespace Api
services.AddControllers(); 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 => services.AddCors(options =>
{ {
options.AddPolicy("AllowAll", builder => options.AddPolicy("AllowAll", builder =>