Implemented api authentication. Removed old secret system
This commit is contained in:
@@ -6,7 +6,6 @@ public class ApiKey
|
|||||||
{
|
{
|
||||||
public int Id { get; set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
public string Secret { get; set; }
|
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
[Column(TypeName="jsonb")]
|
[Column(TypeName="jsonb")]
|
||||||
@@ -14,4 +13,7 @@ public class ApiKey
|
|||||||
|
|
||||||
[Column(TypeName = "timestamp with time zone")]
|
[Column(TypeName = "timestamp with time zone")]
|
||||||
public DateTime ExpiresAt { get; set; }
|
public DateTime ExpiresAt { get; set; }
|
||||||
|
|
||||||
|
[Column(TypeName = "timestamp with time zone")]
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
}
|
}
|
||||||
89
Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.Designer.cs
generated
Normal file
89
Moonlight.ApiServer/Database/Migrations/20250314095412_ModifiedApiKeyEntity.Designer.cs
generated
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Moonlight.ApiServer.Database;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Database.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(CoreDataContext))]
|
||||||
|
[Migration("20250314095412_ModifiedApiKeyEntity")]
|
||||||
|
partial class ModifiedApiKeyEntity
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.11")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.ApiKey", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionsJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Core_ApiKeys", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Moonlight.ApiServer.Database.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("Password")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.Property<string>("PermissionsJson")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
|
b.Property<DateTime>("TokenValidTimestamp")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
|
b.Property<string>("Username")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("text");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Core_Users", (string)null);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Database.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ModifiedApiKeyEntity : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "Secret",
|
||||||
|
table: "Core_ApiKeys");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Core_ApiKeys",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "CreatedAt",
|
||||||
|
table: "Core_ApiKeys");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "Secret",
|
||||||
|
table: "Core_ApiKeys",
|
||||||
|
type: "text",
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,6 +30,9 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone");
|
||||||
|
|
||||||
b.Property<string>("Description")
|
b.Property<string>("Description")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("text");
|
.HasColumnType("text");
|
||||||
@@ -41,10 +44,6 @@ namespace Moonlight.ApiServer.Database.Migrations
|
|||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasColumnType("jsonb");
|
.HasColumnType("jsonb");
|
||||||
|
|
||||||
b.Property<string>("Secret")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("Core_ApiKeys", (string)null);
|
b.ToTable("Core_ApiKeys", (string)null);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using MoonCore.Extended.PermFilter;
|
|||||||
using MoonCore.Helpers;
|
using MoonCore.Helpers;
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
using Moonlight.ApiServer.Services;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
||||||
|
|
||||||
@@ -16,11 +17,13 @@ public class ApiKeysController : Controller
|
|||||||
{
|
{
|
||||||
private readonly CrudHelper<ApiKey, ApiKeyDetailResponse> CrudHelper;
|
private readonly CrudHelper<ApiKey, ApiKeyDetailResponse> CrudHelper;
|
||||||
private readonly DatabaseRepository<ApiKey> ApiKeyRepository;
|
private readonly DatabaseRepository<ApiKey> ApiKeyRepository;
|
||||||
|
private readonly ApiKeyService ApiKeyService;
|
||||||
|
|
||||||
public ApiKeysController(CrudHelper<ApiKey, ApiKeyDetailResponse> crudHelper, DatabaseRepository<ApiKey> apiKeyRepository)
|
public ApiKeysController(CrudHelper<ApiKey, ApiKeyDetailResponse> crudHelper, DatabaseRepository<ApiKey> apiKeyRepository, ApiKeyService apiKeyService)
|
||||||
{
|
{
|
||||||
CrudHelper = crudHelper;
|
CrudHelper = crudHelper;
|
||||||
ApiKeyRepository = apiKeyRepository;
|
ApiKeyRepository = apiKeyRepository;
|
||||||
|
ApiKeyService = apiKeyService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -37,19 +40,20 @@ public class ApiKeysController : Controller
|
|||||||
[RequirePermission("admin.apikeys.create")]
|
[RequirePermission("admin.apikeys.create")]
|
||||||
public async Task<CreateApiKeyResponse> Create([FromBody] CreateApiKeyRequest request)
|
public async Task<CreateApiKeyResponse> Create([FromBody] CreateApiKeyRequest request)
|
||||||
{
|
{
|
||||||
var secret = "api_" + Formatter.GenerateString(32);
|
|
||||||
|
|
||||||
var apiKey = new ApiKey()
|
var apiKey = new ApiKey()
|
||||||
{
|
{
|
||||||
Description = request.Description,
|
Description = request.Description,
|
||||||
PermissionsJson = request.PermissionsJson,
|
PermissionsJson = request.PermissionsJson,
|
||||||
ExpiresAt = request.ExpiresAt,
|
ExpiresAt = request.ExpiresAt
|
||||||
Secret = secret
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var finalApiKey = await ApiKeyRepository.Add(apiKey);
|
var finalApiKey = await ApiKeyRepository.Add(apiKey);
|
||||||
|
|
||||||
return Mapper.Map<CreateApiKeyResponse>(finalApiKey);
|
var response = Mapper.Map<CreateApiKeyResponse>(finalApiKey);
|
||||||
|
|
||||||
|
response.Secret = ApiKeyService.GenerateJwt(finalApiKey);
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Moonlight.ApiServer.Configuration;
|
using Moonlight.ApiServer.Configuration;
|
||||||
using Moonlight.ApiServer.Database;
|
using Moonlight.ApiServer.Database;
|
||||||
using Moonlight.ApiServer.Interfaces.Startup;
|
using Moonlight.ApiServer.Interfaces.Startup;
|
||||||
using Moonlight.ApiServer.Services;
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Implementations.Startup;
|
namespace Moonlight.ApiServer.Implementations.Startup;
|
||||||
|
|
||||||
@@ -25,10 +23,23 @@ public class CoreStartup : IPluginStartup
|
|||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
|
||||||
// Configure swagger api specification generator and set the document title for the api docs to use
|
// Configure swagger api specification generator and set the document title for the api docs to use
|
||||||
builder.Services.AddSwaggerGen(options => options.SwaggerDoc("main", new OpenApiInfo()
|
builder.Services.AddSwaggerGen(options =>
|
||||||
{
|
{
|
||||||
Title = "Moonlight API"
|
options.SwaggerDoc("main", new OpenApiInfo()
|
||||||
}));
|
{
|
||||||
|
Title = "Moonlight API"
|
||||||
|
});
|
||||||
|
|
||||||
|
options.CustomSchemaIds(x => x.FullName);
|
||||||
|
|
||||||
|
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Name = "Authorization",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
Scheme = "Bearer"
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="MoonCore" Version="1.8.5" />
|
<PackageReference Include="MoonCore" Version="1.8.5" />
|
||||||
<PackageReference Include="MoonCore.Extended" Version="1.3.1" />
|
<PackageReference Include="MoonCore.Extended" Version="1.3.2" />
|
||||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.5" />
|
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.5" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||||
|
|||||||
56
Moonlight.ApiServer/Services/ApiKeyService.cs
Normal file
56
Moonlight.ApiServer/Services/ApiKeyService.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using MoonCore.Attributes;
|
||||||
|
using Moonlight.ApiServer.Configuration;
|
||||||
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
|
|
||||||
|
namespace Moonlight.ApiServer.Services;
|
||||||
|
|
||||||
|
[Singleton]
|
||||||
|
public class ApiKeyService
|
||||||
|
{
|
||||||
|
private readonly AppConfiguration Configuration;
|
||||||
|
|
||||||
|
public ApiKeyService(AppConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateJwt(ApiKey apiKey)
|
||||||
|
{
|
||||||
|
var permissions = JsonSerializer.Deserialize<string[]>(apiKey.PermissionsJson) ?? [];
|
||||||
|
|
||||||
|
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
|
||||||
|
var descriptor = new SecurityTokenDescriptor()
|
||||||
|
{
|
||||||
|
Expires = apiKey.ExpiresAt,
|
||||||
|
IssuedAt = DateTime.Now,
|
||||||
|
NotBefore = DateTime.Now.AddMinutes(-1),
|
||||||
|
Claims = new Dictionary<string, object>()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"apiKeyId",
|
||||||
|
apiKey.Id
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions",
|
||||||
|
string.Join(";", permissions)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SigningCredentials = new SigningCredentials(
|
||||||
|
new SymmetricSecurityKey(
|
||||||
|
Encoding.UTF8.GetBytes(Configuration.Authentication.Secret)
|
||||||
|
),
|
||||||
|
SecurityAlgorithms.HmacSha256
|
||||||
|
),
|
||||||
|
Issuer = Configuration.PublicUrl,
|
||||||
|
Audience = Configuration.PublicUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
var securityToken = jwtSecurityTokenHandler.CreateJwtSecurityToken(descriptor);
|
||||||
|
return jwtSecurityTokenHandler.WriteToken(securityToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
using MoonCore.Attributes;
|
|
||||||
using MoonCore.Exceptions;
|
|
||||||
using MoonCore.Extended.Abstractions;
|
|
||||||
using MoonCore.Extended.Helpers;
|
|
||||||
using MoonCore.Services;
|
|
||||||
using Moonlight.ApiServer.Configuration;
|
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.ApiServer.Services;
|
|
||||||
|
|
||||||
[Scoped]
|
|
||||||
public class AuthService
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
|
|
||||||
public AuthService(DatabaseRepository<User> userRepository)
|
|
||||||
{
|
|
||||||
UserRepository = userRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<User> Register(string username, string email, string password)
|
|
||||||
{
|
|
||||||
// Reformat values
|
|
||||||
username = username.ToLower().Trim();
|
|
||||||
email = email.ToLower().Trim();
|
|
||||||
|
|
||||||
// Check for users with the same values
|
|
||||||
if (UserRepository.Get().Any(x => x.Username == username))
|
|
||||||
throw new HttpApiException("A user with that username already exists", 400);
|
|
||||||
|
|
||||||
if (UserRepository.Get().Any(x => x.Email == email))
|
|
||||||
throw new HttpApiException("A user with that email address already exists", 400);
|
|
||||||
|
|
||||||
// Build model and add it to the database
|
|
||||||
var user = new User()
|
|
||||||
{
|
|
||||||
Username = username,
|
|
||||||
Email = email,
|
|
||||||
Password = HashHelper.Hash(password),
|
|
||||||
PermissionsJson = "[]",
|
|
||||||
TokenValidTimestamp = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
UserRepository.Add(user);
|
|
||||||
|
|
||||||
return Task.FromResult(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<User> Login(string email, string password)
|
|
||||||
{
|
|
||||||
// Reformat values
|
|
||||||
email = email.ToLower().Trim();
|
|
||||||
|
|
||||||
var user = UserRepository
|
|
||||||
.Get()
|
|
||||||
.FirstOrDefault(x => x.Email == email);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
throw new HttpApiException("Invalid email or password", 400);
|
|
||||||
|
|
||||||
if(!HashHelper.Verify(password, user.Password))
|
|
||||||
throw new HttpApiException("Invalid email or password", 400);
|
|
||||||
|
|
||||||
return Task.FromResult(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -54,7 +54,8 @@ public class Startup
|
|||||||
|
|
||||||
private IPluginStartup[] PluginStartups;
|
private IPluginStartup[] PluginStartups;
|
||||||
|
|
||||||
public async Task Run(string[] args, Assembly[]? additionalAssemblies = null, PluginManifest[]? additionalManifests = null)
|
public async Task Run(string[] args, Assembly[]? additionalAssemblies = null,
|
||||||
|
PluginManifest[]? additionalManifests = null)
|
||||||
{
|
{
|
||||||
Args = args;
|
Args = args;
|
||||||
AdditionalAssemblies = additionalAssemblies ?? [];
|
AdditionalAssemblies = additionalAssemblies ?? [];
|
||||||
@@ -282,7 +283,7 @@ public class Startup
|
|||||||
{
|
{
|
||||||
var startup = ActivatorUtilities.CreateInstance(startupSp, type) as IPluginStartup;
|
var startup = ActivatorUtilities.CreateInstance(startupSp, type) as IPluginStartup;
|
||||||
|
|
||||||
if(startup == null)
|
if (startup == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
startups.Add(startup);
|
startups.Add(startup);
|
||||||
@@ -520,8 +521,8 @@ public class Startup
|
|||||||
private Task RegisterAuth()
|
private Task RegisterAuth()
|
||||||
{
|
{
|
||||||
WebApplicationBuilder.Services
|
WebApplicationBuilder.Services
|
||||||
.AddAuthentication("userAuthentication")
|
.AddAuthentication("coreAuthentication")
|
||||||
.AddJwtBearer("userAuthentication",options =>
|
.AddJwtBearer("coreAuthentication", options =>
|
||||||
{
|
{
|
||||||
options.TokenValidationParameters = new()
|
options.TokenValidationParameters = new()
|
||||||
{
|
{
|
||||||
@@ -538,20 +539,40 @@ public class Startup
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
WebApplicationBuilder.Services.AddJwtInvalidation("userAuthentication",options =>
|
WebApplicationBuilder.Services.AddJwtInvalidation("coreAuthentication", options =>
|
||||||
{
|
{
|
||||||
options.InvalidateTimeProvider = async (provider, principal) =>
|
options.InvalidateTimeProvider = async (provider, principal) =>
|
||||||
{
|
{
|
||||||
var userIdClaim = principal.Claims.First(x => x.Type == "userId");
|
var userIdClaim = principal.Claims.FirstOrDefault(x => x.Type == "userId");
|
||||||
var userId = int.Parse(userIdClaim.Value);
|
|
||||||
|
|
||||||
var userRepository = provider.GetRequiredService<DatabaseRepository<User>>();
|
if (userIdClaim != null)
|
||||||
var user = await userRepository.Get().FirstOrDefaultAsync(x => x.Id == userId);
|
{
|
||||||
|
var userId = int.Parse(userIdClaim.Value);
|
||||||
|
|
||||||
if(user == null)
|
var userRepository = provider.GetRequiredService<DatabaseRepository<User>>();
|
||||||
return DateTime.MaxValue;
|
var user = await userRepository.Get().FirstOrDefaultAsync(x => x.Id == userId);
|
||||||
|
|
||||||
return user.TokenValidTimestamp;
|
if (user == null)
|
||||||
|
return DateTime.MaxValue;
|
||||||
|
|
||||||
|
return user.TokenValidTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiKeyIdClaim = principal.Claims.FirstOrDefault(x => x.Type == "apiKeyId");
|
||||||
|
|
||||||
|
if (apiKeyIdClaim != null)
|
||||||
|
{
|
||||||
|
var apiKeyId = int.Parse(apiKeyIdClaim.Value);
|
||||||
|
|
||||||
|
var apiKeyRepository = provider.GetRequiredService<DatabaseRepository<ApiKey>>();
|
||||||
|
var apiKey = await apiKeyRepository.Get().FirstOrDefaultAsync(x => x.Id == apiKeyId);
|
||||||
|
|
||||||
|
// If the api key exists, we don't want to invalidate the request.
|
||||||
|
// If it doesn't exist we want to invalidate the request
|
||||||
|
return apiKey == null ? DateTime.MaxValue : DateTime.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DateTime.MaxValue;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -88,12 +88,26 @@ public class SysFileSystemProvider : IFileSystemProvider, ICompressFileSystemPro
|
|||||||
|
|
||||||
public async Task Upload(Func<long, Task> updateProgress, string path, Stream stream)
|
public async Task Upload(Func<long, Task> updateProgress, string path, Stream stream)
|
||||||
{
|
{
|
||||||
var progressStream = new ProgressStream(stream, onReadChanged: new Progress<long>(async bytes =>
|
var cts = new CancellationTokenSource();
|
||||||
{
|
|
||||||
await updateProgress.Invoke(bytes);
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Create(path, progressStream);
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
while (!cts.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
await updateProgress.Invoke(stream.Position);
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(500), cts.Token);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Create(path, stream);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
// Ensure we aren't creating an endless loop ^^
|
||||||
|
await cts.CancelAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Compress(CompressType type, string path, string[] itemsToCompress)
|
public async Task Compress(CompressType type, string path, string[] itemsToCompress)
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ public class CreateApiKeyRequest
|
|||||||
public string PermissionsJson { get; set; } = "[]";
|
public string PermissionsJson { get; set; } = "[]";
|
||||||
|
|
||||||
[Required(ErrorMessage = "You need to specify an expire date")]
|
[Required(ErrorMessage = "You need to specify an expire date")]
|
||||||
public DateTime ExpiresAt { get; set; } = DateTime.Now.AddDays(30);
|
public DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddDays(30);
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user