mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 21:32:27 +01:00
Use ClaimTransformer instead of Authentication's JWT
This commit is contained in:
parent
7e5c593e09
commit
fda6a1a77b
@ -13,7 +13,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.0.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="3.0.0-rc1.final" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19503.62" />
|
||||
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="3.0.0-alpha1.19509.59" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -238,7 +238,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
private async Task WaitSiteIsOperational()
|
||||
{
|
||||
using (var cts = new CancellationTokenSource(10_000))
|
||||
using (var cts = new CancellationTokenSource(20_000))
|
||||
{
|
||||
var synching = WaitIsFullySynched(cts.Token);
|
||||
var accessingHomepage = WaitCanAccessHomepage(cts.Token);
|
||||
|
@ -33,6 +33,7 @@ namespace BTCPayServer.Authentication
|
||||
}
|
||||
|
||||
public const string CanViewStores = nameof(CanViewStores);
|
||||
public const string CanEditStore = nameof(CanEditStore);
|
||||
public const string CanManageStores = nameof(CanManageStores);
|
||||
public const string CanViewInvoices = nameof(CanViewInvoices);
|
||||
public const string CanCreateInvoices = nameof(CanCreateInvoices);
|
||||
@ -47,6 +48,7 @@ namespace BTCPayServer.Authentication
|
||||
AddScopePolicy(options, CanViewStores,
|
||||
context => context.HasScopes(BTCPayScopes.StoreManagement) ||
|
||||
context.HasScopes(BTCPayScopes.ViewStores));
|
||||
options.AddPolicy(CanEditStore, p => p.RequireClaim(CanEditStore));
|
||||
AddScopePolicy(options, CanManageStores,
|
||||
context => context.HasScopes(BTCPayScopes.StoreManagement));
|
||||
AddScopePolicy(options, CanViewInvoices,
|
||||
|
@ -26,8 +26,6 @@ namespace BTCPayServer.Authentication.OpenId
|
||||
ApplicationUser user,
|
||||
AuthenticationProperties properties = null)
|
||||
{
|
||||
// Create a new ClaimsPrincipal containing the claims that
|
||||
// will be used to create an id_token, a token or a code.
|
||||
var principal = await signInManager.CreateUserPrincipalAsync(user);
|
||||
|
||||
// Create a new authentication ticket holding the user identity.
|
||||
|
@ -57,7 +57,7 @@ namespace BTCPayServer.Controllers.RestApi
|
||||
|
||||
|
||||
[HttpGet("me/stores/{storeId}/can-edit")]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings.Key,
|
||||
[Authorize(Policy = RestAPIPolicies.CanEditStore,
|
||||
AuthenticationSchemes = OpenIddictValidationDefaults.AuthenticationScheme)]
|
||||
public bool CanEdit(string storeId)
|
||||
{
|
||||
|
@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Authentication;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
@ -53,6 +53,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace BTCPayServer.Hosting
|
||||
{
|
||||
@ -175,6 +176,7 @@ namespace BTCPayServer.Hosting
|
||||
return htmlSanitizer;
|
||||
});
|
||||
|
||||
services.AddTransient<IClaimsTransformation, ClaimTransformer>();
|
||||
services.TryAddSingleton<LightningConfigurationProvider>();
|
||||
services.TryAddSingleton<LanguageService>();
|
||||
services.TryAddSingleton<NBXplorerDashboard>();
|
||||
@ -285,43 +287,6 @@ namespace BTCPayServer.Hosting
|
||||
options.TokenValidationParameters.IssuerSigningKey =
|
||||
OpenIddictExtensions.GetSigningKey(configuration);
|
||||
options.IncludeErrorDetails = true;
|
||||
options.Events = new JwtBearerEvents()
|
||||
{
|
||||
OnTokenValidated = async context =>
|
||||
{
|
||||
var routeData = context.HttpContext.GetRouteData();
|
||||
var identity = ((ClaimsIdentity)context.Principal.Identity);
|
||||
if (context.Principal.IsInRole(Roles.ServerAdmin))
|
||||
{
|
||||
identity.AddClaim(new Claim(Policies.CanModifyServerSettings.Key, "true"));
|
||||
}
|
||||
|
||||
if (context.HttpContext.GetStoreData() != null ||
|
||||
!routeData.Values.TryGetValue("storeId", out var storeId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userManager = context.HttpContext.RequestServices
|
||||
.GetService<UserManager<ApplicationUser>>();
|
||||
var storeRepository = context.HttpContext.RequestServices
|
||||
.GetService<StoreRepository>();
|
||||
var userid = userManager.GetUserId(context.Principal);
|
||||
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
var store = await storeRepository.FindStore((string)storeId, userid);
|
||||
if (store == null)
|
||||
{
|
||||
context.Fail("Could not authorize you against store access");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.HttpContext.SetStoreData(store);
|
||||
identity.AddClaims(store.GetClaims());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})
|
||||
.AddCookie()
|
||||
.AddBitpayAuthentication();
|
||||
|
66
BTCPayServer/Security/ClaimTransformer.cs
Normal file
66
BTCPayServer/Security/ClaimTransformer.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Stores;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using BTCPayServer.Authentication;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
public class ClaimTransformer : IClaimsTransformation
|
||||
{
|
||||
private readonly HttpContext _HttpContext;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly StoreRepository _storeRepository;
|
||||
|
||||
public ClaimTransformer(IHttpContextAccessor httpContextAccessor,
|
||||
UserManager<ApplicationUser> userManager,
|
||||
StoreRepository storeRepository)
|
||||
{
|
||||
_HttpContext = httpContextAccessor.HttpContext;
|
||||
_userManager = userManager;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
var routeData = _HttpContext.GetRouteData();
|
||||
if (routeData == null)
|
||||
return principal;
|
||||
var identity = ((ClaimsIdentity)principal.Identity);
|
||||
// A ClaimTransform can be called several time, we prevent dups by removing all the
|
||||
// claims this transform might add.
|
||||
var claims = new[] { RestAPIPolicies.CanEditStore };
|
||||
foreach (var claim in identity.Claims.Where(c => claims.Contains(c.Type)).ToList())
|
||||
{
|
||||
identity.RemoveClaim(claim);
|
||||
}
|
||||
|
||||
if (!routeData.Values.TryGetValue("storeId", out var storeId))
|
||||
{
|
||||
return principal;
|
||||
}
|
||||
var userid = _userManager.GetUserId(principal);
|
||||
if (!string.IsNullOrEmpty(userid))
|
||||
{
|
||||
var store = await _storeRepository.FindStore((string)storeId, userid);
|
||||
if (store != null)
|
||||
{
|
||||
_HttpContext.SetStoreData(store);
|
||||
foreach (var claim in store.GetClaims())
|
||||
{
|
||||
if (claim.Type.Equals(Policies.CanModifyStoreSettings.Key, System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
identity.AddClaim(new Claim(RestAPIPolicies.CanEditStore, store.Id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user