Use ClaimTransformer instead of Authentication's JWT

This commit is contained in:
nicolas.dorier 2019-10-10 13:42:39 +09:00
parent 7e5c593e09
commit fda6a1a77b
No known key found for this signature in database
GPG Key ID: 6618763EF09186FE
8 changed files with 74 additions and 42 deletions

View File

@ -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>

View File

@ -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);

View File

@ -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,

View File

@ -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.

View File

@ -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)
{

View File

@ -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;

View File

@ -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();

View 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;
}
}
}