App domain redirect (#4391)

* Fix duplicates in GetAllApps with allowNoUser

* Use domain mapping as canonical reference and redirect to it

* Revert domain mapping to hostname instead of URL
This commit is contained in:
d11n
2023-02-02 12:53:42 +01:00
committed by GitHub
parent 99299ba06f
commit 26248774c2
4 changed files with 58 additions and 38 deletions

View File

@@ -843,6 +843,7 @@ namespace BTCPayServer.Tests
select = new SelectElement(s.Driver.FindElement(By.Id("DomainToAppMapping_0__AppId")));
select.SelectByText("Point of", true);
s.Driver.FindElement(By.Id("SaveButton")).Click();
Assert.Contains("Policies updated successfully", s.FindAlertMessage().Text);
s.Logout();
s.LogIn(userId);

View File

@@ -758,22 +758,21 @@ namespace BTCPayServer.Controllers
{
return Redirect(returnUrl);
}
else
{
// After login, if there is an app on "/", we should redirect to BTCPay explicit home route, and not to the app.
if (PoliciesSettings.RootAppId is not null && PoliciesSettings.RootAppType is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
if (PoliciesSettings.DomainToAppMapping is { } mapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(this.HttpContext.Request.Host.Host, StringComparison.InvariantCultureIgnoreCase));
if (matchedDomainMapping is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
}
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
}
}
// After login, if there is an app on "/", we should redirect to BTCPay explicit home route, and not to the app.
if (PoliciesSettings.RootAppId is not null && PoliciesSettings.RootAppType is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
if (PoliciesSettings.DomainToAppMapping is { } mapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(HttpContext.Request.Host.Host, StringComparison.InvariantCultureIgnoreCase));
if (matchedDomainMapping is not null)
return RedirectToAction(nameof(UIHomeController.Home), "UIHome");
}
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
}
private bool CanLoginOrRegister()
{

View File

@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Abstractions.Contracts;
using BTCPayServer.HostedServices;
using BTCPayServer.Services;
using BTCPayServer.Services.Apps;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
using Microsoft.Extensions.DependencyInjection;
@@ -15,45 +12,62 @@ namespace BTCPayServer.Filters
{
public DomainMappingConstraintAttribute()
{
}
public DomainMappingConstraintAttribute(AppType appType)
{
AppType = appType;
}
public int Order => 100;
public AppType? AppType { get; set; }
private AppType? AppType { get; }
public bool Accept(ActionConstraintContext context)
{
if (context.RouteContext.RouteData.Values.ContainsKey("appId"))
return true;
var hasAppId = context.RouteContext.RouteData.Values.ContainsKey("appId");
var policies = context.RouteContext.HttpContext.RequestServices.GetService<PoliciesSettings>();
if (policies?.DomainToAppMapping is { } mapping)
var mapping = policies?.DomainToAppMapping;
var hasDomainMapping = mapping is { Count: > 0 };
if (hasAppId && hasDomainMapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host, StringComparison.InvariantCultureIgnoreCase));
var appId = (string)context.RouteContext.RouteData.Values["appId"];
var matchedDomainMapping = mapping.FirstOrDefault(item => item.AppId == appId);
// App is accessed via path, redirect to canonical domain
if (matchedDomainMapping != null)
{
if (!(AppType is { } appType))
var req = context.RouteContext.HttpContext.Request;
var url = new UriBuilder(req.Scheme, matchedDomainMapping.Domain).ToString();
context.RouteContext.HttpContext.Response.Redirect(url);
return true;
}
}
if (hasDomainMapping)
{
var matchedDomainMapping = mapping.FirstOrDefault(item =>
item.Domain.Equals(context.RouteContext.HttpContext.Request.Host.Host,
StringComparison.InvariantCultureIgnoreCase));
if (matchedDomainMapping != null)
{
if (AppType is not { } appType)
return false;
if (appType != matchedDomainMapping.AppType)
return false;
context.RouteContext.RouteData.Values.Add("appId", matchedDomainMapping.AppId);
return true;
}
if (AppType == policies.RootAppType)
{
context.RouteContext.RouteData.Values.Add("appId", policies.RootAppId);
return true;
}
return AppType is null;
}
return AppType is null;
if (AppType == policies.RootAppType)
{
context.RouteContext.RouteData.Values.Add("appId", policies.RootAppId);
return true;
}
return hasAppId || AppType is null;
}
}
}

View File

@@ -443,7 +443,7 @@ namespace BTCPayServer.Services.Apps
(storeId == null || us.StoreDataId == storeId))
.Join(ctx.Apps, us => us.StoreDataId, app => app.StoreDataId,
(us, app) =>
new ListAppsViewModel.ListAppViewModel()
new ListAppsViewModel.ListAppViewModel
{
IsOwner = us.Role == StoreRoles.Owner,
StoreId = us.StoreDataId,
@@ -455,6 +455,12 @@ namespace BTCPayServer.Services.Apps
})
.OrderBy(b => b.Created)
.ToArrayAsync();
// allowNoUser can lead to apps being included twice, unify them with distinct
if (allowNoUser)
{
listApps = listApps.DistinctBy(a => a.Id).ToArray();
}
foreach (ListAppsViewModel.ListAppViewModel app in listApps)
{