Compare commits
4 Commits
02bfbabcfc
...
595ade2d79
Author | SHA1 | Date | |
---|---|---|---|
|
595ade2d79 | ||
|
8ad863b781 | ||
|
46de83dc42 | ||
|
edc195b234 |
@ -1,7 +1,9 @@
|
||||
using Api.DBAccess;
|
||||
using Api.Models;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
@ -11,34 +13,27 @@ namespace Api.AMQP
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly DbAccess _dbAccess;
|
||||
private IConnection _conn;
|
||||
private IChannel _channel;
|
||||
private ConnectionFactory _factory;
|
||||
private string _queue;
|
||||
|
||||
public AMQPPublisher(IConfiguration configuration, DbAccess dbAccess)
|
||||
{
|
||||
_dbAccess = dbAccess;
|
||||
_configuration = configuration;
|
||||
_factory = new ConnectionFactory();
|
||||
_queue = "temperature-limits";
|
||||
|
||||
InitFactory();
|
||||
}
|
||||
|
||||
public async Task Handle_Push_Device_Limits()
|
||||
{
|
||||
var factory = new ConnectionFactory();
|
||||
var queue = "temperature-limits";
|
||||
|
||||
factory.UserName = _configuration["AMQP:username"];
|
||||
factory.Password = _configuration["AMQP:password"];
|
||||
factory.HostName = _configuration["AMQP:host"];
|
||||
factory.Port = Convert.ToInt32(_configuration["AMQP:port"]);
|
||||
|
||||
// Connecting to our rabbitmq and after that it create's a channel where you can connect to a queue
|
||||
var conn = await factory.CreateConnectionAsync();
|
||||
Console.WriteLine("AMQPClient connected");
|
||||
var channel = await conn.CreateChannelAsync();
|
||||
|
||||
// Here we connect to the queue through the channel that got created earlier
|
||||
await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false);
|
||||
Console.WriteLine($"{queue} connected");
|
||||
|
||||
while (true)
|
||||
{
|
||||
await Connect();
|
||||
|
||||
// Publishes all devices limits
|
||||
var devices = _dbAccess.ReadDevices();
|
||||
foreach (var device in devices)
|
||||
@ -49,33 +44,21 @@ namespace Api.AMQP
|
||||
deviceLimit.TempLow = device.TempLow;
|
||||
string message = JsonSerializer.Serialize(deviceLimit);
|
||||
var body = Encoding.UTF8.GetBytes(message);
|
||||
await channel.BasicPublishAsync(exchange: string.Empty, routingKey: queue, body: body);
|
||||
await _channel.BasicPublishAsync(exchange: string.Empty, routingKey: _queue, body: body);
|
||||
}
|
||||
|
||||
// Short delay before disconnecting from rabbitMQ
|
||||
await Task.Delay(10000);
|
||||
await Task.Delay(1000);
|
||||
|
||||
// Disconnecting from rabbitMQ to save resources
|
||||
await channel.CloseAsync();
|
||||
Console.WriteLine($"{queue} disconnected");
|
||||
await conn.CloseAsync();
|
||||
Console.WriteLine("AMQPClient disconnected");
|
||||
await channel.DisposeAsync();
|
||||
await conn.DisposeAsync();
|
||||
await Dispose();
|
||||
// 1 hour delay
|
||||
await Task.Delay(3600000);
|
||||
|
||||
// Creating a new connection to rabbitMQ
|
||||
conn = await factory.CreateConnectionAsync();
|
||||
Console.WriteLine("AMQPClient connected");
|
||||
channel = await conn.CreateChannelAsync();
|
||||
|
||||
// Here we connect to the queue through the channel that got created earlier
|
||||
await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false);
|
||||
Console.WriteLine($"{queue} connected");
|
||||
await Connect();
|
||||
|
||||
// Here all messages is consumed so the queue is empty
|
||||
var consumer = new AsyncEventingBasicConsumer(channel);
|
||||
var consumer = new AsyncEventingBasicConsumer(_channel);
|
||||
consumer.ReceivedAsync += (model, ea) =>
|
||||
{
|
||||
Console.WriteLine("Emptying queue");
|
||||
@ -84,9 +67,45 @@ namespace Api.AMQP
|
||||
};
|
||||
|
||||
// Consumes the data in the queue
|
||||
await channel.BasicConsumeAsync(queue, true, consumer);
|
||||
await _channel.BasicConsumeAsync(_queue, true, consumer);
|
||||
|
||||
// Short delay before disconnecting from rabbitMQ
|
||||
await Task.Delay(1000);
|
||||
await Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnects from rabbitMQ
|
||||
private async Task<bool> Dispose()
|
||||
{
|
||||
await _channel.CloseAsync();
|
||||
await _conn.CloseAsync();
|
||||
await _channel.DisposeAsync();
|
||||
await _conn.DisposeAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Connects to rabbitMQ
|
||||
private async Task<bool> Connect()
|
||||
{
|
||||
// Creating a new connection to rabbitMQ
|
||||
_conn = await _factory.CreateConnectionAsync();
|
||||
Console.WriteLine("AMQPClient connected");
|
||||
_channel = await _conn.CreateChannelAsync();
|
||||
|
||||
// Here we connect to the queue through the channel that got created earlier
|
||||
await _channel.QueueDeclareAsync(queue: _queue, durable: false, exclusive: false, autoDelete: false);
|
||||
Console.WriteLine($"{_queue} connected");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The info for the factory
|
||||
private void InitFactory()
|
||||
{
|
||||
_factory.UserName = _configuration["AMQP:username"];
|
||||
_factory.Password = _configuration["AMQP:password"];
|
||||
_factory.HostName = _configuration["AMQP:host"];
|
||||
_factory.Port = Convert.ToInt32(_configuration["AMQP:port"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using RabbitMQ.Client;
|
||||
using RabbitMQ.Client.Events;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Api.AMQPReciever
|
||||
{
|
||||
@ -11,34 +12,27 @@ namespace Api.AMQPReciever
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly DbAccess _dbAccess;
|
||||
private IConnection _conn;
|
||||
private IChannel _channel;
|
||||
private ConnectionFactory _factory;
|
||||
private string _queue;
|
||||
|
||||
public AMQPReciever(IConfiguration configuration, DbAccess dbAccess)
|
||||
{
|
||||
_dbAccess = dbAccess;
|
||||
_configuration = configuration;
|
||||
_factory = new ConnectionFactory();
|
||||
_queue = "temperature-logs";
|
||||
|
||||
InitFactory();
|
||||
}
|
||||
|
||||
public async Task Handle_Received_Application_Message()
|
||||
{
|
||||
var factory = new ConnectionFactory();
|
||||
var queue = "temperature-logs";
|
||||
|
||||
factory.UserName = _configuration["AMQP:username"];
|
||||
factory.Password = _configuration["AMQP:password"];
|
||||
factory.HostName = _configuration["AMQP:host"];
|
||||
factory.Port = Convert.ToInt32(_configuration["AMQP:port"]);
|
||||
|
||||
// Connecting to our rabbitmq and after that it create's a channel where you can connect to a queue
|
||||
using var conn = await factory.CreateConnectionAsync();
|
||||
Console.WriteLine("AMQPClient connected");
|
||||
using var channel = await conn.CreateChannelAsync();
|
||||
|
||||
// Here we connect to the queue through the channel that got created earlier
|
||||
await channel.QueueDeclareAsync(queue: queue, durable: false, exclusive: false, autoDelete: false);
|
||||
Console.WriteLine($"{queue} connected");
|
||||
await Connect();
|
||||
|
||||
// Everytime a message is recieved from the queue it goes into this consumer.ReceivedAsync
|
||||
var consumer = new AsyncEventingBasicConsumer(channel);
|
||||
var consumer = new AsyncEventingBasicConsumer(_channel);
|
||||
consumer.ReceivedAsync += (model, ea) =>
|
||||
{
|
||||
Console.WriteLine("Received application message.");
|
||||
@ -73,9 +67,44 @@ namespace Api.AMQPReciever
|
||||
};
|
||||
|
||||
// Consumes the data in the queue
|
||||
await channel.BasicConsumeAsync(queue, true, consumer);
|
||||
await _channel.BasicConsumeAsync(_queue, true, consumer);
|
||||
|
||||
while (true);
|
||||
|
||||
await Dispose();
|
||||
}
|
||||
|
||||
// Disconnects from rabbitMQ
|
||||
private async Task<bool> Dispose()
|
||||
{
|
||||
await _channel.CloseAsync();
|
||||
await _conn.CloseAsync();
|
||||
await _channel.DisposeAsync();
|
||||
await _conn.DisposeAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Connects to rabbitMQ
|
||||
private async Task<bool> Connect()
|
||||
{
|
||||
// Creating a new connection to rabbitMQ
|
||||
_conn = await _factory.CreateConnectionAsync();
|
||||
Console.WriteLine("AMQPClient connected");
|
||||
_channel = await _conn.CreateChannelAsync();
|
||||
|
||||
// Here we connect to the queue through the channel that got created earlier
|
||||
await _channel.QueueDeclareAsync(queue: _queue, durable: false, exclusive: false, autoDelete: false);
|
||||
Console.WriteLine($"{_queue} connected");
|
||||
return true;
|
||||
}
|
||||
|
||||
// The info for the factory
|
||||
private void InitFactory()
|
||||
{
|
||||
_factory.UserName = _configuration["AMQP:username"];
|
||||
_factory.Password = _configuration["AMQP:password"];
|
||||
_factory.HostName = _configuration["AMQP:host"];
|
||||
_factory.Port = Convert.ToInt32(_configuration["AMQP:port"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ namespace Api.BusinessLogic
|
||||
|
||||
string salt = Guid.NewGuid().ToString();
|
||||
string hashedPassword = ComputeHash(user.Password, SHA256.Create(), salt);
|
||||
|
||||
|
||||
user.Salt = salt;
|
||||
user.Password = hashedPassword;
|
||||
|
||||
@ -74,7 +74,9 @@ namespace Api.BusinessLogic
|
||||
if (user.Password == hashedPassword)
|
||||
{
|
||||
var token = GenerateJwtToken(user);
|
||||
return new OkObjectResult(new { token, user.UserName, user.Id });
|
||||
user.RefreshToken = Guid.NewGuid().ToString();
|
||||
_dbAccess.UpdatesRefreshToken(user.RefreshToken, user.Id);
|
||||
return new OkObjectResult(new { token, user.UserName, user.Id, refreshToken = user.RefreshToken });
|
||||
}
|
||||
|
||||
return new ConflictObjectResult(new { message = "Invalid password" });
|
||||
@ -119,6 +121,13 @@ namespace Api.BusinessLogic
|
||||
return await _dbAccess.DeleteUser(userId);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> RefreshToken(string refreshToken)
|
||||
{
|
||||
User user = await _dbAccess.ReadUser(refreshToken);
|
||||
if (user == null) { return new ConflictObjectResult(new { message = "Could not match refreshtoken" }); }
|
||||
return new OkObjectResult(GenerateJwtToken(user));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a hash from a salt and input using the algorithm that is provided
|
||||
/// </summary>
|
||||
|
@ -51,5 +51,11 @@ namespace Api.Controllers
|
||||
return await _userLogic.DeleteUser(userId);
|
||||
}
|
||||
|
||||
[HttpGet("RefreshToken")]
|
||||
public async Task<IActionResult> RefreshToken(string refreshToken)
|
||||
{
|
||||
return await _userLogic.RefreshToken(refreshToken);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,20 @@ namespace Api.DBAccess
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
}
|
||||
|
||||
// Returns a user according to refreshToken
|
||||
public async Task<User> ReadUser(string refreshToken)
|
||||
{
|
||||
return await _context.Users.FirstOrDefaultAsync(u => u.RefreshToken == refreshToken);
|
||||
}
|
||||
|
||||
public async void UpdatesRefreshToken(string refreshToken, int userId)
|
||||
{
|
||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);
|
||||
|
||||
user.RefreshToken = refreshToken;
|
||||
user.RefreshTokenExpiresAt = DateTime.Now.AddDays(7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the user in the database
|
||||
/// </summary>
|
||||
|
138
backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.Designer.cs
generated
Normal file
138
backend/Api/Migrations/20250327084557_AddedRefreshTokenToUser.Designer.cs
generated
Normal file
@ -0,0 +1,138 @@
|
||||
// <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("20250327084557_AddedRefreshTokenToUser")]
|
||||
partial class AddedRefreshTokenToUser
|
||||
{
|
||||
/// <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>("RefreshToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("RefreshTokenExpiresAt")
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Api.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddedRefreshTokenToUser : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "RefreshToken",
|
||||
table: "Users",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "RefreshTokenExpiresAt",
|
||||
table: "Users",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RefreshToken",
|
||||
table: "Users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "RefreshTokenExpiresAt",
|
||||
table: "Users");
|
||||
}
|
||||
}
|
||||
}
|
@ -88,6 +88,12 @@ namespace Api.Migrations
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RefreshToken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("RefreshTokenExpiresAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Salt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
|
@ -12,6 +12,10 @@
|
||||
|
||||
public string? Salt { get; set; }
|
||||
|
||||
public string? RefreshToken { get; set; }
|
||||
|
||||
public DateTime RefreshTokenExpiresAt { get; set; }
|
||||
|
||||
public List<Device>? Devices { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,29 @@ namespace Api
|
||||
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[] { }
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user