diff --git a/Pronto-MIA/Pronto-MIA/BusinessLogic/API/Interceptors/UserStateRequestInterceptor.cs b/Pronto-MIA/Pronto-MIA/BusinessLogic/API/Interceptors/UserStateRequestInterceptor.cs
index 72df92d..52f7686 100644
--- a/Pronto-MIA/Pronto-MIA/BusinessLogic/API/Interceptors/UserStateRequestInterceptor.cs
+++ b/Pronto-MIA/Pronto-MIA/BusinessLogic/API/Interceptors/UserStateRequestInterceptor.cs
@@ -1,7 +1,116 @@
namespace Pronto_MIA.BusinessLogic.API.Interceptors
{
- public class UserStateRequestInterceptor
+ using System;
+ using System.Globalization;
+ using System.Linq;
+ using System.Security.Authentication;
+ using System.Security.Claims;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using HotChocolate;
+ using HotChocolate.Execution;
+ using Microsoft.AspNetCore.Http;
+ using Microsoft.EntityFrameworkCore;
+ using Microsoft.Extensions.Configuration;
+ using Pronto_MIA.BusinessLogic.API.Types;
+ using Pronto_MIA.DataAccess;
+ using Pronto_MIA.Domain.Entities;
+ using Pronto_MIA.Services;
+
+ ///
+ /// Class containing the interceptor method used to add the
+ /// to every request.
+ ///
+ public static class UserStateRequestInterceptor
{
-
+ ///
+ /// Adds to every request so that
+ /// information on the requesting user is available if necessary.
+ /// Also checks if the user is existing and allowed to authenticate.
+ ///
+ /// The configuration of the application used
+ /// to create a dbContext.
+ /// The httpContext of the current request.
+ ///
+ /// The executor of the current request.
+ /// The query request builder which will be
+ /// extended by the .
+ /// The cancellation token of the request.
+ /// A task that can be awaited.
+ /// Throws a query exception if
+ /// the user could not be found or the token used for authentication
+ /// has been invalidated.
+ public static async ValueTask AddUserState(
+ IConfiguration cfg,
+ HttpContext context,
+ IRequestExecutor executor,
+ IQueryRequestBuilder builder,
+ CancellationToken token)
+ {
+ ApiUserState apiUserState = default;
+ var identity = context.User.Identity;
+ if (identity is { IsAuthenticated: true })
+ {
+ var user = await GetUser(cfg, context);
+ CheckIfTokenRevoked(context, user.LastInvalidated);
+ apiUserState = new ApiUserState(user);
+ }
+
+ builder.SetProperty(
+ ApiUserGlobalState.ApiUserStateName,
+ apiUserState);
+ }
+
+ private static async Task GetUser(
+ IConfiguration cfg,
+ HttpContext context)
+ {
+ await using (var dbContext = new ProntoMiaDbContext(
+ DatabaseService.GetOptions(cfg)))
+ {
+ var userId = int.Parse(context
+ .User.FindFirstValue(ClaimTypes.NameIdentifier));
+ var userName = context
+ .User.FindFirstValue(ClaimTypes.Name);
+ var user = dbContext.Users
+ .Include(u => u.Department)
+ .Include(u => u.AccessControlList)
+ .SingleOrDefault(u => u.Id == userId);
+ if (user == null)
+ {
+ throw GetAuthorizationException();
+ }
+
+ return user;
+ }
+ }
+
+ private static void CheckIfTokenRevoked(
+ HttpContext context,
+ DateTime lastInvalidated)
+ {
+ var issuedAt = context
+ .User.FindFirstValue("issuedAt");
+ if (issuedAt == null)
+ {
+ throw GetAuthorizationException();
+ }
+
+ var issuedAtTime = DateTime.Parse(
+ issuedAt, CultureInfo.InvariantCulture);
+ if (lastInvalidated > issuedAtTime)
+ {
+ throw new AuthenticationException();
+ }
+ }
+
+ private static GraphQLException GetAuthorizationException()
+ {
+ return new (
+ ErrorBuilder.New()
+ .SetMessage("The current user is not authorized " +
+ "to access this resource.")
+ .SetCode("AUTH_NOT_AUTHORIZED").Build());
+ }
}
-}
\ No newline at end of file
+}
diff --git a/Pronto-MIA/Pronto-MIA/DataAccess/Managers/UserManager.cs b/Pronto-MIA/Pronto-MIA/DataAccess/Managers/UserManager.cs
index ec2c34f..a4d9be2 100644
--- a/Pronto-MIA/Pronto-MIA/DataAccess/Managers/UserManager.cs
+++ b/Pronto-MIA/Pronto-MIA/DataAccess/Managers/UserManager.cs
@@ -4,6 +4,7 @@ namespace Pronto_MIA.DataAccess.Managers
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
@@ -220,6 +221,8 @@ private string GenerateToken(User user)
new Claim(
ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
+ new Claim("issuedAt", DateTime.UtcNow.ToString(
+ CultureInfo.InvariantCulture)),
};
var token = new JwtSecurityToken(
@@ -298,6 +301,7 @@ private void UpdatePassword(string password, User user)
this.CheckPasswordPolicy(password);
var generator = HashGeneratorFactory.GetGeneratorForUser(user);
user.PasswordHash = generator.HashPassword(password);
+ user.LastInvalidated = DateTime.UtcNow;
}
///
diff --git a/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.Designer.cs b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.Designer.cs
new file mode 100644
index 0000000..c7025f1
--- /dev/null
+++ b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.Designer.cs
@@ -0,0 +1,280 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+using Pronto_MIA.DataAccess;
+
+namespace Pronto_MIA.DataAccess.Migrations
+{
+ [DbContext(typeof(ProntoMiaDbContext))]
+ [Migration("20210603094129_TokenInvalidation-add")]
+ partial class TokenInvalidationadd
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("Relational:MaxIdentifierLength", 63)
+ .HasAnnotation("ProductVersion", "5.0.6")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.AccessControlList", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property("CanEditDepartmentDeploymentPlans")
+ .HasColumnType("boolean");
+
+ b.Property("CanEditDepartmentUsers")
+ .HasColumnType("boolean");
+
+ b.Property("CanEditDepartments")
+ .HasColumnType("boolean");
+
+ b.Property("CanEditDeploymentPlans")
+ .HasColumnType("boolean");
+
+ b.Property("CanEditOwnDepartment")
+ .HasColumnType("boolean");
+
+ b.Property("CanEditUsers")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewDepartmentDeploymentPlans")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewDepartmentUsers")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewDepartments")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewDeploymentPlans")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewOwnDepartment")
+ .HasColumnType("boolean");
+
+ b.Property("CanViewUsers")
+ .HasColumnType("boolean");
+
+ b.Property("UserId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("AccessControlLists");
+
+ b.HasData(
+ new
+ {
+ Id = -1,
+ CanEditDepartmentDeploymentPlans = false,
+ CanEditDepartmentUsers = false,
+ CanEditDepartments = true,
+ CanEditDeploymentPlans = true,
+ CanEditOwnDepartment = false,
+ CanEditUsers = true,
+ CanViewDepartmentDeploymentPlans = false,
+ CanViewDepartmentUsers = false,
+ CanViewDepartments = true,
+ CanViewDeploymentPlans = true,
+ CanViewOwnDepartment = false,
+ CanViewUsers = true,
+ UserId = -1
+ });
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.Department", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Name")
+ .IsUnique();
+
+ b.ToTable("Departments");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.DeploymentPlan", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property("AvailableFrom")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property("AvailableUntil")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property("DepartmentId")
+ .HasColumnType("integer");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("FileExtension")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FileUuid")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Published")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DepartmentId");
+
+ b.HasIndex("FileUuid")
+ .IsUnique();
+
+ b.ToTable("DeploymentPlans");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.FcmToken", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("OwnerId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("OwnerId");
+
+ b.ToTable("FcmTokens");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
+
+ b.Property("DepartmentId")
+ .HasColumnType("integer");
+
+ b.Property("HashGenerator")
+ .HasColumnType("text");
+
+ b.Property("HashGeneratorOptions")
+ .IsRequired()
+ .HasColumnType("jsonb");
+
+ b.Property("LastInvalidated")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp without time zone")
+ .HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
+ b.Property("PasswordHash")
+ .IsRequired()
+ .HasColumnType("bytea");
+
+ b.Property("UserName")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DepartmentId");
+
+ b.HasIndex("UserName")
+ .IsUnique();
+
+ b.ToTable("Users");
+
+ b.HasData(
+ new
+ {
+ Id = -1,
+ HashGenerator = "Pbkdf2Generator",
+ HashGeneratorOptions = "{\"SaltSize\":128,\"HashIterations\":1500,\"HashSize\":512,\"Salt\":\"A+16bv/SvaC7ZJgS7u+CB8nN32PBUAbJuT09NigsCzQx6/CxS1I/5laUaFoJNZ3QhTm4TqFnWYzokdrvrUxbOEN0MN3ZhINcblSLF9LwbZeiT0nYOnQgTBEPL0KoszXdm8x2mYXHAJFYQ9KOsIZregzuiBQfSqsFfR2uDnFHm9o=\"}",
+ LastInvalidated = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
+ PasswordHash = new byte[] { 160, 170, 181, 57, 56, 11, 64, 111, 255, 135, 44, 16, 13, 143, 10, 57, 47, 144, 79, 128, 47, 6, 240, 159, 91, 163, 33, 239, 51, 140, 5, 59, 228, 56, 155, 57, 18, 151, 143, 191, 223, 64, 140, 19, 23, 125, 90, 34, 241, 76, 199, 118, 197, 240, 49, 56, 110, 38, 182, 112, 19, 172, 195, 113, 47, 223, 184, 44, 27, 214, 16, 242, 169, 148, 104, 135, 116, 43, 152, 103, 130, 206, 221, 110, 254, 123, 231, 102, 42, 239, 67, 130, 53, 113, 12, 150, 249, 50, 117, 225, 113, 38, 174, 80, 246, 112, 27, 104, 229, 197, 63, 76, 246, 218, 33, 63, 197, 145, 244, 65, 91, 81, 228, 81, 208, 91, 117, 249, 101, 112, 179, 10, 130, 136, 0, 168, 150, 224, 43, 124, 151, 107, 110, 10, 23, 83, 212, 80, 45, 113, 201, 148, 17, 114, 46, 169, 232, 169, 138, 135, 89, 53, 154, 213, 123, 208, 0, 155, 155, 0, 44, 249, 199, 222, 211, 120, 137, 158, 135, 20, 247, 225, 64, 119, 64, 177, 76, 43, 106, 59, 205, 69, 30, 104, 84, 115, 252, 213, 154, 16, 235, 86, 107, 165, 86, 125, 87, 171, 100, 92, 114, 185, 85, 117, 119, 147, 128, 31, 168, 227, 83, 203, 123, 182, 229, 205, 165, 114, 12, 231, 171, 5, 15, 199, 227, 175, 168, 225, 180, 30, 90, 122, 175, 224, 109, 166, 93, 79, 69, 82, 95, 47, 78, 64, 213, 105, 13, 104, 183, 153, 8, 239, 49, 170, 233, 125, 79, 93, 78, 154, 51, 210, 50, 108, 169, 27, 204, 101, 47, 232, 179, 174, 45, 14, 234, 165, 167, 76, 171, 213, 98, 251, 82, 8, 195, 74, 161, 64, 111, 78, 101, 146, 217, 143, 43, 248, 254, 233, 54, 140, 96, 182, 10, 27, 227, 64, 121, 70, 19, 161, 37, 249, 7, 73, 27, 215, 160, 207, 19, 172, 124, 6, 176, 71, 16, 75, 32, 92, 143, 100, 188, 175, 189, 227, 113, 249, 235, 68, 238, 151, 60, 134, 209, 189, 104, 1, 219, 157, 84, 149, 179, 50, 219, 8, 81, 188, 68, 194, 8, 98, 118, 135, 197, 212, 153, 226, 240, 162, 98, 253, 63, 125, 29, 112, 194, 162, 113, 175, 5, 162, 114, 208, 107, 177, 202, 88, 127, 196, 166, 82, 5, 61, 254, 6, 172, 248, 243, 140, 155, 93, 246, 184, 238, 132, 207, 112, 120, 79, 140, 30, 224, 112, 197, 209, 228, 90, 194, 214, 42, 229, 85, 167, 27, 12, 85, 179, 197, 131, 120, 158, 57, 251, 14, 55, 0, 244, 42, 240, 134, 91, 107, 152, 201, 46, 104, 3, 94, 191, 76, 114, 242, 42, 28, 120, 121, 161, 215, 5, 23, 57, 171, 150, 191, 224, 86, 25, 248, 120, 176, 240, 37, 151, 195, 74, 208, 146, 104, 159, 34, 147, 107, 239, 156, 198, 190, 97, 196, 132, 15, 54, 33, 75, 152, 78, 166, 227, 174, 236, 74, 67, 237, 184 },
+ UserName = "Admin"
+ });
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.AccessControlList", b =>
+ {
+ b.HasOne("Pronto_MIA.Domain.Entities.User", "User")
+ .WithOne("AccessControlList")
+ .HasForeignKey("Pronto_MIA.Domain.Entities.AccessControlList", "UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.DeploymentPlan", b =>
+ {
+ b.HasOne("Pronto_MIA.Domain.Entities.Department", "Department")
+ .WithMany("DeploymentPlans")
+ .HasForeignKey("DepartmentId")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.Navigation("Department");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.FcmToken", b =>
+ {
+ b.HasOne("Pronto_MIA.Domain.Entities.User", "Owner")
+ .WithMany("FcmTokens")
+ .HasForeignKey("OwnerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Owner");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.User", b =>
+ {
+ b.HasOne("Pronto_MIA.Domain.Entities.Department", "Department")
+ .WithMany("Users")
+ .HasForeignKey("DepartmentId")
+ .OnDelete(DeleteBehavior.SetNull);
+
+ b.Navigation("Department");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.Department", b =>
+ {
+ b.Navigation("DeploymentPlans");
+
+ b.Navigation("Users");
+ });
+
+ modelBuilder.Entity("Pronto_MIA.Domain.Entities.User", b =>
+ {
+ b.Navigation("AccessControlList");
+
+ b.Navigation("FcmTokens");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.cs b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.cs
new file mode 100644
index 0000000..33094cc
--- /dev/null
+++ b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/20210603094129_TokenInvalidation-add.cs
@@ -0,0 +1,28 @@
+//
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace Pronto_MIA.DataAccess.Migrations
+{
+ [ExcludeFromCodeCoverage]
+ public partial class TokenInvalidationadd : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "LastInvalidated",
+ table: "Users",
+ type: "timestamp without time zone",
+ nullable: false,
+ defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "LastInvalidated",
+ table: "Users");
+ }
+ }
+}
diff --git a/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/ProntoMiaDbContextModelSnapshot.cs b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/ProntoMiaDbContextModelSnapshot.cs
index 370b8bd..99e8ed8 100644
--- a/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/ProntoMiaDbContextModelSnapshot.cs
+++ b/Pronto-MIA/Pronto-MIA/DataAccess/Migrations/ProntoMiaDbContextModelSnapshot.cs
@@ -185,6 +185,11 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("jsonb");
+ b.Property("LastInvalidated")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("timestamp without time zone")
+ .HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
+
b.Property("PasswordHash")
.IsRequired()
.HasColumnType("bytea");
@@ -208,6 +213,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
Id = -1,
HashGenerator = "Pbkdf2Generator",
HashGeneratorOptions = "{\"SaltSize\":128,\"HashIterations\":1500,\"HashSize\":512,\"Salt\":\"A+16bv/SvaC7ZJgS7u+CB8nN32PBUAbJuT09NigsCzQx6/CxS1I/5laUaFoJNZ3QhTm4TqFnWYzokdrvrUxbOEN0MN3ZhINcblSLF9LwbZeiT0nYOnQgTBEPL0KoszXdm8x2mYXHAJFYQ9KOsIZregzuiBQfSqsFfR2uDnFHm9o=\"}",
+ LastInvalidated = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
PasswordHash = new byte[] { 160, 170, 181, 57, 56, 11, 64, 111, 255, 135, 44, 16, 13, 143, 10, 57, 47, 144, 79, 128, 47, 6, 240, 159, 91, 163, 33, 239, 51, 140, 5, 59, 228, 56, 155, 57, 18, 151, 143, 191, 223, 64, 140, 19, 23, 125, 90, 34, 241, 76, 199, 118, 197, 240, 49, 56, 110, 38, 182, 112, 19, 172, 195, 113, 47, 223, 184, 44, 27, 214, 16, 242, 169, 148, 104, 135, 116, 43, 152, 103, 130, 206, 221, 110, 254, 123, 231, 102, 42, 239, 67, 130, 53, 113, 12, 150, 249, 50, 117, 225, 113, 38, 174, 80, 246, 112, 27, 104, 229, 197, 63, 76, 246, 218, 33, 63, 197, 145, 244, 65, 91, 81, 228, 81, 208, 91, 117, 249, 101, 112, 179, 10, 130, 136, 0, 168, 150, 224, 43, 124, 151, 107, 110, 10, 23, 83, 212, 80, 45, 113, 201, 148, 17, 114, 46, 169, 232, 169, 138, 135, 89, 53, 154, 213, 123, 208, 0, 155, 155, 0, 44, 249, 199, 222, 211, 120, 137, 158, 135, 20, 247, 225, 64, 119, 64, 177, 76, 43, 106, 59, 205, 69, 30, 104, 84, 115, 252, 213, 154, 16, 235, 86, 107, 165, 86, 125, 87, 171, 100, 92, 114, 185, 85, 117, 119, 147, 128, 31, 168, 227, 83, 203, 123, 182, 229, 205, 165, 114, 12, 231, 171, 5, 15, 199, 227, 175, 168, 225, 180, 30, 90, 122, 175, 224, 109, 166, 93, 79, 69, 82, 95, 47, 78, 64, 213, 105, 13, 104, 183, 153, 8, 239, 49, 170, 233, 125, 79, 93, 78, 154, 51, 210, 50, 108, 169, 27, 204, 101, 47, 232, 179, 174, 45, 14, 234, 165, 167, 76, 171, 213, 98, 251, 82, 8, 195, 74, 161, 64, 111, 78, 101, 146, 217, 143, 43, 248, 254, 233, 54, 140, 96, 182, 10, 27, 227, 64, 121, 70, 19, 161, 37, 249, 7, 73, 27, 215, 160, 207, 19, 172, 124, 6, 176, 71, 16, 75, 32, 92, 143, 100, 188, 175, 189, 227, 113, 249, 235, 68, 238, 151, 60, 134, 209, 189, 104, 1, 219, 157, 84, 149, 179, 50, 219, 8, 81, 188, 68, 194, 8, 98, 118, 135, 197, 212, 153, 226, 240, 162, 98, 253, 63, 125, 29, 112, 194, 162, 113, 175, 5, 162, 114, 208, 107, 177, 202, 88, 127, 196, 166, 82, 5, 61, 254, 6, 172, 248, 243, 140, 155, 93, 246, 184, 238, 132, 207, 112, 120, 79, 140, 30, 224, 112, 197, 209, 228, 90, 194, 214, 42, 229, 85, 167, 27, 12, 85, 179, 197, 131, 120, 158, 57, 251, 14, 55, 0, 244, 42, 240, 134, 91, 107, 152, 201, 46, 104, 3, 94, 191, 76, 114, 242, 42, 28, 120, 121, 161, 215, 5, 23, 57, 171, 150, 191, 224, 86, 25, 248, 120, 176, 240, 37, 151, 195, 74, 208, 146, 104, 159, 34, 147, 107, 239, 156, 198, 190, 97, 196, 132, 15, 54, 33, 75, 152, 78, 166, 227, 174, 236, 74, 67, 237, 184 },
UserName = "Admin"
});
diff --git a/Pronto-MIA/Pronto-MIA/Domain/Entities/User.cs b/Pronto-MIA/Pronto-MIA/Domain/Entities/User.cs
index 3c37689..f2fda89 100644
--- a/Pronto-MIA/Pronto-MIA/Domain/Entities/User.cs
+++ b/Pronto-MIA/Pronto-MIA/Domain/Entities/User.cs
@@ -1,5 +1,6 @@
namespace Pronto_MIA.Domain.Entities
{
+ using System;
using System.Collections.Generic;
using HotChocolate;
using HotChocolate.AspNetCore.Authorization;
@@ -102,5 +103,11 @@ protected User()
///
[GraphQLIgnore]
public virtual ICollection FcmTokens { get; set; }
+
+ ///
+ /// Gets or sets the time the users jwt-tokens were last invalidated.
+ ///
+ [GraphQLIgnore]
+ public DateTime LastInvalidated { get; set; }
}
}
diff --git a/Pronto-MIA/Pronto-MIA/Domain/EntityTypeConfigs/UserTypeConfig.cs b/Pronto-MIA/Pronto-MIA/Domain/EntityTypeConfigs/UserTypeConfig.cs
index 3a0180d..9f3ee61 100644
--- a/Pronto-MIA/Pronto-MIA/Domain/EntityTypeConfigs/UserTypeConfig.cs
+++ b/Pronto-MIA/Pronto-MIA/Domain/EntityTypeConfigs/UserTypeConfig.cs
@@ -1,5 +1,6 @@
namespace Pronto_MIA.Domain.EntityTypeConfigs
{
+ using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Pronto_MIA.Domain.Entities;
@@ -25,6 +26,9 @@ public void Configure(EntityTypeBuilder builder)
builder.Property(u => u.HashGeneratorOptions)
.IsRequired()
.HasColumnType("jsonb");
+ builder.Property(u => u.LastInvalidated)
+ .IsRequired()
+ .HasDefaultValue(default(DateTime));
builder.HasMany(u => u.FcmTokens)
.WithOne(t => t.Owner);
builder.HasOne(u => u.AccessControlList)
diff --git a/Pronto-MIA/Pronto-MIA/Services/GraphQLService.cs b/Pronto-MIA/Pronto-MIA/Services/GraphQLService.cs
index 8346b56..56346c5 100644
--- a/Pronto-MIA/Pronto-MIA/Services/GraphQLService.cs
+++ b/Pronto-MIA/Pronto-MIA/Services/GraphQLService.cs
@@ -1,17 +1,13 @@
namespace Pronto_MIA.Services
{
using System.Diagnostics.CodeAnalysis;
- using System.Security.Claims;
- using System.Threading;
- using System.Threading.Tasks;
- using HotChocolate.Execution;
using HotChocolate.Types;
- using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Pronto_MIA.BusinessLogic.API;
using Pronto_MIA.BusinessLogic.API.EntityExtensions;
+ using Pronto_MIA.BusinessLogic.API.Interceptors;
using Pronto_MIA.BusinessLogic.API.Logging;
using Pronto_MIA.BusinessLogic.API.Types;
using Pronto_MIA.BusinessLogic.API.Types.Mutation;
@@ -38,12 +34,12 @@ public static void AddGraphQLService(this IServiceCollection services)
{
var serviceProvider = services.BuildServiceProvider();
var config = serviceProvider.GetService();
-
services.AddGraphQLServer()
.AddAuthorization()
.AddHttpRequestInterceptor(
(context, executor, builder, token) =>
- AddUserState(config, context, executor, builder, token))
+ UserStateRequestInterceptor.AddUserState(
+ config, context, executor, builder, token))
.AddType()
.AddType()
.AddType()
@@ -64,39 +60,5 @@ public static void AddGraphQLService(this IServiceCollection services)
new QueryLogger(
sp.GetApplicationService>()));
}
-
- ///
- /// Adds user information to the global state cache so that it may be
- /// accessed in a query.
- ///
- private static ValueTask AddUserState(
- IConfiguration cfg,
- HttpContext context,
- IRequestExecutor executor,
- IQueryRequestBuilder builder,
- CancellationToken token)
- {
- var dbContext = new ProntoMiaDbContext(
- DatabaseService.GetOptions(cfg));
-
- ApiUserState apiUserState = default;
- var identity = context.User.Identity;
- if (identity is { IsAuthenticated: true })
- {
- var userId = int.Parse(context
- .User.FindFirstValue(ClaimTypes.NameIdentifier));
- var userName = context
- .User.FindFirstValue(ClaimTypes.Name);
- var user = dbContext.Users.Find(userId);
-
- apiUserState = new ApiUserState(user);
- }
-
- builder.SetProperty(
- ApiUserGlobalState.ApiUserStateName,
- apiUserState);
-
- return ValueTask.CompletedTask;
- }
}
}