Switch to JWT authentication (#19)
* switch to jwt authentication * fix delete files * adjust saving of deletion of all files * update api to main/jwt Co-authored-by: rootdarkarchon <root.darkarchon@outlook.com>
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
"MainServerGrpcAddress": "",
|
"MainServerGrpcAddress": "",
|
||||||
"FailedAuthForTempBan": 5,
|
"FailedAuthForTempBan": 5,
|
||||||
"TempBanDurationInMinutes": 5,
|
"TempBanDurationInMinutes": 5,
|
||||||
|
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
|
||||||
"WhitelistedIps": [
|
"WhitelistedIps": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"MainServerGrpcAddress": "",
|
"MainServerGrpcAddress": "",
|
||||||
"FailedAuthForTempBan": 5,
|
"FailedAuthForTempBan": 5,
|
||||||
"TempBanDurationInMinutes": 5,
|
"TempBanDurationInMinutes": 5,
|
||||||
|
"Jwt": "teststringteststringteststringteststringteststringteststringteststringteststringteststringteststring",
|
||||||
"WhitelistedIps": [
|
"WhitelistedIps": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
|||||||
2
MareAPI
2
MareAPI
Submodule MareAPI updated: c01c4990d4...6645eaf63f
@@ -0,0 +1,63 @@
|
|||||||
|
using MareSynchronos.API;
|
||||||
|
using MareSynchronosShared;
|
||||||
|
using MareSynchronosShared.Authentication;
|
||||||
|
using MareSynchronosShared.Services;
|
||||||
|
using MareSynchronosShared.Utils;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace MareSynchronosServer.Controllers;
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[Route(MareAuth.Auth)]
|
||||||
|
public class JwtController : Controller
|
||||||
|
{
|
||||||
|
private readonly IHttpContextAccessor _accessor;
|
||||||
|
private readonly SecretKeyAuthenticatorService _secretKeyAuthenticatorService;
|
||||||
|
private readonly IConfigurationService<MareConfigurationAuthBase> _configuration;
|
||||||
|
|
||||||
|
public JwtController(IHttpContextAccessor accessor, SecretKeyAuthenticatorService secretKeyAuthenticatorService, IConfigurationService<MareConfigurationAuthBase> configuration)
|
||||||
|
{
|
||||||
|
_accessor = accessor;
|
||||||
|
_secretKeyAuthenticatorService = secretKeyAuthenticatorService;
|
||||||
|
_configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
[HttpPost(MareAuth.AuthCreate)]
|
||||||
|
public async Task<IActionResult> CreateToken(string auth)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(auth)) return BadRequest("No Authkey");
|
||||||
|
|
||||||
|
var ip = _accessor.GetIpAddress();
|
||||||
|
|
||||||
|
var authResult = await _secretKeyAuthenticatorService.AuthorizeAsync(ip, auth);
|
||||||
|
|
||||||
|
if (!authResult.Success) return Unauthorized("Invalid Authkey");
|
||||||
|
|
||||||
|
var token = CreateToken(new List<Claim>()
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, authResult.Uid)
|
||||||
|
});
|
||||||
|
|
||||||
|
return Content(token.RawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtSecurityToken CreateToken(IEnumerable<Claim> authClaims)
|
||||||
|
{
|
||||||
|
var authSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_configuration.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt))));
|
||||||
|
|
||||||
|
var token = new SecurityTokenDescriptor()
|
||||||
|
{
|
||||||
|
Subject = new ClaimsIdentity(authClaims),
|
||||||
|
SigningCredentials = new SigningCredentials(authSigningKey, SecurityAlgorithms.HmacSha256Signature)
|
||||||
|
};
|
||||||
|
|
||||||
|
var handler = new JwtSecurityTokenHandler();
|
||||||
|
return handler.CreateJwtSecurityToken(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using Grpc.Core;
|
using Grpc.Core;
|
||||||
@@ -9,6 +8,7 @@ using MareSynchronosShared.Models;
|
|||||||
using MareSynchronosShared.Protos;
|
using MareSynchronosShared.Protos;
|
||||||
using MareSynchronosShared.Utils;
|
using MareSynchronosShared.Utils;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace MareSynchronosServer.Hubs;
|
namespace MareSynchronosServer.Hubs;
|
||||||
@@ -37,7 +37,7 @@ public partial class MareHub
|
|||||||
request.Hash.AddRange(ownFiles.Select(f => f.Hash));
|
request.Hash.AddRange(ownFiles.Select(f => f.Hash));
|
||||||
Metadata headers = new Metadata()
|
Metadata headers = new Metadata()
|
||||||
{
|
{
|
||||||
{ "Authorization", Context.User!.Claims.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.Authentication, StringComparison.Ordinal))?.Value }
|
{ "Authorization", Context.GetHttpContext().Request.Headers["Authorization"].ToString() }
|
||||||
};
|
};
|
||||||
_ = await _fileServiceClient.DeleteFilesAsync(request, headers).ConfigureAwait(false);
|
_ = await _fileServiceClient.DeleteFilesAsync(request, headers).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@ public partial class MareHub
|
|||||||
|
|
||||||
Metadata headers = new Metadata()
|
Metadata headers = new Metadata()
|
||||||
{
|
{
|
||||||
{ "Authorization", Context.User!.Claims.SingleOrDefault(c => string.Equals(c.Type, ClaimTypes.Authentication, StringComparison.Ordinal))?.Value }
|
{ "Authorization", Context.GetHttpContext().Request.Headers["Authorization"].ToString() }
|
||||||
};
|
};
|
||||||
var streamingCall = _fileServiceClient.UploadFile(headers);
|
var streamingCall = _fileServiceClient.UploadFile(headers);
|
||||||
using var tempFileStream = new FileStream(tempFileName, FileMode.Open, FileAccess.Read);
|
using var tempFileStream = new FileStream(tempFileName, FileMode.Open, FileAccess.Read);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace MareSynchronosServer.Hubs;
|
namespace MareSynchronosServer.Hubs;
|
||||||
|
|
||||||
|
[Authorize(Policy = "Authenticated")]
|
||||||
public partial class MareHub : Hub<IMareHub>, IMareHub
|
public partial class MareHub : Hub<IMareHub>, IMareHub
|
||||||
{
|
{
|
||||||
private readonly MareMetrics _mareMetrics;
|
private readonly MareMetrics _mareMetrics;
|
||||||
@@ -118,6 +119,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
return Task.FromResult(needsReconnect);
|
return Task.FromResult(needsReconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Authenticated")]
|
||||||
public override async Task OnConnectedAsync()
|
public override async Task OnConnectedAsync()
|
||||||
{
|
{
|
||||||
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress()));
|
_logger.LogCallInfo(MareHubLogger.Args(_contextAccessor.GetIpAddress()));
|
||||||
@@ -125,6 +127,7 @@ public partial class MareHub : Hub<IMareHub>, IMareHub
|
|||||||
await base.OnConnectedAsync().ConfigureAwait(false);
|
await base.OnConnectedAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Authorize(Policy = "Authenticated")]
|
||||||
public override async Task OnDisconnectedAsync(Exception exception)
|
public override async Task OnDisconnectedAsync(Exception exception)
|
||||||
{
|
{
|
||||||
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
|
_mareMetrics.DecGauge(MetricsAPI.GaugeConnections);
|
||||||
|
|||||||
@@ -31,11 +31,14 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.25.1" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using MareSynchronos.API;
|
using MareSynchronos.API;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MareSynchronosServer.Hubs;
|
using MareSynchronosServer.Hubs;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Http.Connections;
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
@@ -20,6 +19,9 @@ using MareSynchronosShared.Services;
|
|||||||
using Prometheus;
|
using Prometheus;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Grpc.Net.ClientFactory;
|
using Grpc.Net.ClientFactory;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace MareSynchronosServer;
|
namespace MareSynchronosServer;
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ public class Startup
|
|||||||
ConfigureMareServices(services, mareConfig);
|
ConfigureMareServices(services, mareConfig);
|
||||||
|
|
||||||
services.AddHealthChecks();
|
services.AddHealthChecks();
|
||||||
|
services.AddControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureMareServices(IServiceCollection services, IConfigurationSection mareConfig)
|
private static void ConfigureMareServices(IServiceCollection services, IConfigurationSection mareConfig)
|
||||||
@@ -121,17 +124,35 @@ public class Startup
|
|||||||
{
|
{
|
||||||
services.AddSingleton<SecretKeyAuthenticatorService>();
|
services.AddSingleton<SecretKeyAuthenticatorService>();
|
||||||
services.AddTransient<IAuthorizationHandler, UserRequirementHandler>();
|
services.AddTransient<IAuthorizationHandler, UserRequirementHandler>();
|
||||||
services.AddAuthentication(SecretKeyAuthenticationHandler.AuthScheme)
|
|
||||||
.AddScheme<AuthenticationSchemeOptions, SecretKeyAuthenticationHandler>(SecretKeyAuthenticationHandler.AuthScheme, options => { options.Validate(); });
|
services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.Configure<IConfigurationService<MareConfigurationAuthBase>>((o, s) =>
|
||||||
|
{
|
||||||
|
o.TokenValidationParameters = new()
|
||||||
|
{
|
||||||
|
ValidateIssuer = false,
|
||||||
|
ValidateLifetime = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(s.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt))))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAuthentication(o =>
|
||||||
|
{
|
||||||
|
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
}).AddJwtBearer();
|
||||||
|
|
||||||
services.AddAuthorization(options =>
|
services.AddAuthorization(options =>
|
||||||
{
|
{
|
||||||
options.DefaultPolicy = new AuthorizationPolicyBuilder()
|
options.DefaultPolicy = new AuthorizationPolicyBuilder()
|
||||||
.AddAuthenticationSchemes(SecretKeyAuthenticationHandler.AuthScheme)
|
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.RequireAuthenticatedUser().Build();
|
.RequireAuthenticatedUser().Build();
|
||||||
options.AddPolicy("Authenticated", policy =>
|
options.AddPolicy("Authenticated", policy =>
|
||||||
{
|
{
|
||||||
policy.AddAuthenticationSchemes(SecretKeyAuthenticationHandler.AuthScheme);
|
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
|
||||||
policy.RequireAuthenticatedUser();
|
policy.RequireAuthenticatedUser();
|
||||||
});
|
});
|
||||||
options.AddPolicy("Identified", policy =>
|
options.AddPolicy("Identified", policy =>
|
||||||
@@ -306,7 +327,8 @@ public class Startup
|
|||||||
endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous();
|
endpoints.MapGrpcService<GrpcConfigurationService<ServerConfiguration>>().AllowAnonymous();
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints.MapHealthChecks("/health").WithMetadata(new AllowAnonymousAttribute());
|
endpoints.MapHealthChecks("/health").AllowAnonymous();
|
||||||
|
endpoints.MapControllers().AllowAnonymous();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
namespace MareSynchronosShared.Authentication;
|
namespace MareSynchronosShared.Authentication;
|
||||||
|
|
||||||
internal record SecretKeyAuthReply(bool Success, string? Uid);
|
public record SecretKeyAuthReply(bool Success, string? Uid);
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace MareSynchronosShared.Authentication;
|
|
||||||
|
|
||||||
public class SecretKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
|
||||||
{
|
|
||||||
public const string AuthScheme = "SecretKeyGrpcAuth";
|
|
||||||
|
|
||||||
private readonly IHttpContextAccessor _accessor;
|
|
||||||
private readonly SecretKeyAuthenticatorService secretKeyAuthenticatorService;
|
|
||||||
private static readonly ConcurrentDictionary<string, SemaphoreSlim> IPLocks = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
public SecretKeyAuthenticationHandler(IHttpContextAccessor accessor, SecretKeyAuthenticatorService secretKeyAuthenticatorService,
|
|
||||||
IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
|
|
||||||
{
|
|
||||||
_accessor = accessor;
|
|
||||||
this.secretKeyAuthenticatorService = secretKeyAuthenticatorService;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
|
||||||
{
|
|
||||||
var endpoint = Context.GetEndpoint();
|
|
||||||
if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
|
|
||||||
{
|
|
||||||
return AuthenticateResult.NoResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Request.Headers.TryGetValue("Authorization", out var authHeader))
|
|
||||||
{
|
|
||||||
return AuthenticateResult.Fail("Failed Authorization");
|
|
||||||
}
|
|
||||||
|
|
||||||
var ip = _accessor.GetIpAddress();
|
|
||||||
|
|
||||||
if (!IPLocks.TryGetValue(ip, out var semaphore))
|
|
||||||
{
|
|
||||||
semaphore = new SemaphoreSlim(1);
|
|
||||||
IPLocks[ip] = semaphore;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await semaphore.WaitAsync(Context.RequestAborted).ConfigureAwait(false);
|
|
||||||
var authResult = await secretKeyAuthenticatorService.AuthorizeAsync(ip, authHeader).ConfigureAwait(false);
|
|
||||||
|
|
||||||
if (!authResult.Success)
|
|
||||||
{
|
|
||||||
return AuthenticateResult.Fail("Failed Authorization");
|
|
||||||
}
|
|
||||||
|
|
||||||
var claims = new List<Claim>
|
|
||||||
{
|
|
||||||
new(ClaimTypes.NameIdentifier, authResult.Uid),
|
|
||||||
new(ClaimTypes.Authentication, authHeader)
|
|
||||||
};
|
|
||||||
|
|
||||||
var identity = new ClaimsIdentity(claims, nameof(SecretKeyAuthenticationHandler));
|
|
||||||
var principal = new ClaimsPrincipal(identity);
|
|
||||||
var ticket = new AuthenticationTicket(principal, Scheme.Name);
|
|
||||||
|
|
||||||
return AuthenticateResult.Success(ticket);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
semaphore.Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,6 @@ using MareSynchronosShared.Utils;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
|
|
||||||
namespace MareSynchronosShared.Authentication;
|
namespace MareSynchronosShared.Authentication;
|
||||||
|
|
||||||
@@ -27,11 +26,11 @@ public class SecretKeyAuthenticatorService
|
|||||||
_serviceScopeFactory = serviceScopeFactory;
|
_serviceScopeFactory = serviceScopeFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<SecretKeyAuthReply> AuthorizeAsync(string ip, string secretKey)
|
public async Task<SecretKeyAuthReply> AuthorizeAsync(string ip, string hashedSecretKey)
|
||||||
{
|
{
|
||||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests);
|
_metrics.IncCounter(MetricsAPI.CounterAuthenticationRequests);
|
||||||
|
|
||||||
if (_cachedPositiveResponses.TryGetValue(secretKey, out var cachedPositiveResponse))
|
if (_cachedPositiveResponses.TryGetValue(hashedSecretKey, out var cachedPositiveResponse))
|
||||||
{
|
{
|
||||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationCacheHits);
|
_metrics.IncCounter(MetricsAPI.CounterAuthenticationCacheHits);
|
||||||
return cachedPositiveResponse;
|
return cachedPositiveResponse;
|
||||||
@@ -58,8 +57,7 @@ public class SecretKeyAuthenticatorService
|
|||||||
|
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = _serviceScopeFactory.CreateScope();
|
||||||
using var context = scope.ServiceProvider.GetService<MareDbContext>();
|
using var context = scope.ServiceProvider.GetService<MareDbContext>();
|
||||||
var hashedHeader = StringUtils.Sha256String(secretKey);
|
var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedSecretKey).ConfigureAwait(false);
|
||||||
var authReply = await context.Auth.AsNoTracking().SingleOrDefaultAsync(u => u.HashedKey == hashedHeader).ConfigureAwait(false);
|
|
||||||
|
|
||||||
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID);
|
SecretKeyAuthReply reply = new(authReply != null, authReply?.UserUID);
|
||||||
|
|
||||||
@@ -67,11 +65,11 @@ public class SecretKeyAuthenticatorService
|
|||||||
{
|
{
|
||||||
_metrics.IncCounter(MetricsAPI.CounterAuthenticationSuccesses);
|
_metrics.IncCounter(MetricsAPI.CounterAuthenticationSuccesses);
|
||||||
|
|
||||||
_cachedPositiveResponses[secretKey] = reply;
|
_cachedPositiveResponses[hashedSecretKey] = reply;
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
|
await Task.Delay(TimeSpan.FromMinutes(5)).ConfigureAwait(false);
|
||||||
_cachedPositiveResponses.TryRemove(secretKey, out _);
|
_cachedPositiveResponses.TryRemove(hashedSecretKey, out _);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public class MareConfigurationAuthBase : MareConfigurationBase
|
|||||||
public int TempBanDurationInMinutes { get; set; } = 5;
|
public int TempBanDurationInMinutes { get; set; } = 5;
|
||||||
[RemoteConfiguration]
|
[RemoteConfiguration]
|
||||||
public List<string> WhitelistedIps { get; set; } = new();
|
public List<string> WhitelistedIps { get; set; } = new();
|
||||||
|
[RemoteConfiguration]
|
||||||
|
public string Jwt { get; set; } = string.Empty;
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@@ -19,6 +21,7 @@ public class MareConfigurationAuthBase : MareConfigurationBase
|
|||||||
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
sb.AppendLine($"{nameof(MainServerGrpcAddress)} => {MainServerGrpcAddress}");
|
||||||
sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}");
|
sb.AppendLine($"{nameof(FailedAuthForTempBan)} => {FailedAuthForTempBan}");
|
||||||
sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}");
|
sb.AppendLine($"{nameof(TempBanDurationInMinutes)} => {TempBanDurationInMinutes}");
|
||||||
|
sb.AppendLine($"{nameof(Jwt)} => {Jwt}");
|
||||||
sb.AppendLine($"{nameof(WhitelistedIps)} => {string.Join(", ", WhitelistedIps)}");
|
sb.AppendLine($"{nameof(WhitelistedIps)} => {string.Join(", ", WhitelistedIps)}");
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,14 +78,16 @@ public class GrpcFileService : FileService.FileServiceBase
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var fi = FilePathUtil.GetFileInfoForHash(_basePath, hash);
|
var fi = FilePathUtil.GetFileInfoForHash(_basePath, hash);
|
||||||
fi?.Delete();
|
|
||||||
var file = await _mareDbContext.Files.SingleOrDefaultAsync(f => f.Hash == hash).ConfigureAwait(false);
|
var file = await _mareDbContext.Files.SingleOrDefaultAsync(f => f.Hash == hash).ConfigureAwait(false);
|
||||||
if (file != null)
|
if (file != null && fi != null)
|
||||||
{
|
{
|
||||||
_mareDbContext.Files.Remove(file);
|
_mareDbContext.Files.Remove(file);
|
||||||
|
await _mareDbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
_metricsClient.DecGauge(MetricsAPI.GaugeFilesTotal, fi == null ? 0 : 1);
|
_metricsClient.DecGauge(MetricsAPI.GaugeFilesTotal, fi == null ? 0 : 1);
|
||||||
_metricsClient.DecGauge(MetricsAPI.GaugeFilesTotalSize, fi?.Length ?? 0);
|
_metricsClient.DecGauge(MetricsAPI.GaugeFilesTotalSize, fi?.Length ?? 0);
|
||||||
|
|
||||||
|
fi?.Delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -94,7 +96,6 @@ public class GrpcFileService : FileService.FileServiceBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await _mareDbContext.SaveChangesAsync().ConfigureAwait(false);
|
|
||||||
return new Empty();
|
return new Empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ using MareSynchronosShared.Protos;
|
|||||||
using MareSynchronosShared.Services;
|
using MareSynchronosShared.Services;
|
||||||
using MareSynchronosShared.Utils;
|
using MareSynchronosShared.Utils;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Prometheus;
|
using Prometheus;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace MareSynchronosStaticFilesServer;
|
namespace MareSynchronosStaticFilesServer;
|
||||||
|
|
||||||
@@ -101,11 +104,26 @@ public class Startup
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddAuthentication(options =>
|
services.AddOptions<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme)
|
||||||
|
.Configure<IConfigurationService<MareConfigurationAuthBase>>((o, s) =>
|
||||||
{
|
{
|
||||||
options.DefaultScheme = SecretKeyAuthenticationHandler.AuthScheme;
|
o.TokenValidationParameters = new()
|
||||||
})
|
{
|
||||||
.AddScheme<AuthenticationSchemeOptions, SecretKeyAuthenticationHandler>(SecretKeyAuthenticationHandler.AuthScheme, options => { });
|
ValidateIssuer = false,
|
||||||
|
ValidateLifetime = false,
|
||||||
|
ValidateAudience = false,
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(s.GetValue<string>(nameof(MareConfigurationAuthBase.Jwt))))
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAuthentication(o =>
|
||||||
|
{
|
||||||
|
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||||
|
}).AddJwtBearer();
|
||||||
|
|
||||||
services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
|
services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build());
|
||||||
|
|
||||||
if (_isMain)
|
if (_isMain)
|
||||||
|
|||||||
Reference in New Issue
Block a user