Browse Source

Merge pull request #18492 from abpframework/AppUrlProvider

Make `IsRedirectAllowedUrlAsync` of `IAppUrlProvider` async.
pull/18675/head
liangshiwei 3 years ago
committed by GitHub
parent
commit
5d66b03b20
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs
  2. 21
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs
  3. 22
      framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs
  4. 10
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs
  5. 4
      framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs
  6. 46
      framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AppUrlProvider_Tests.cs
  7. 2
      modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs
  8. 18
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs
  9. 6
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs
  10. 2
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs
  11. 2
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs
  12. 6
      modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml.cs

22
framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs

@ -122,40 +122,40 @@ public abstract class AbpPageModel : PageModel
return localizer;
}
protected RedirectResult RedirectSafely(string returnUrl, string? returnUrlHash = null)
protected virtual async Task<RedirectResult> RedirectSafelyAsync(string returnUrl, string? returnUrlHash = null)
{
return Redirect(GetRedirectUrl(returnUrl, returnUrlHash));
return Redirect(await GetRedirectUrlAsync(returnUrl, returnUrlHash));
}
protected virtual string GetRedirectUrl(string returnUrl, string? returnUrlHash = null)
protected virtual async Task<string> GetRedirectUrlAsync(string returnUrl, string? returnUrlHash = null)
{
returnUrl = NormalizeReturnUrl(returnUrl);
returnUrl = await NormalizeReturnUrlAsync(returnUrl);
if (!returnUrlHash.IsNullOrWhiteSpace())
{
returnUrl = returnUrl + returnUrlHash;
returnUrl += returnUrlHash;
}
return returnUrl;
}
private string NormalizeReturnUrl(string returnUrl)
protected virtual async Task<string> NormalizeReturnUrlAsync(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return GetAppHomeUrl();
return await GetAppHomeUrlAsync();
}
if (Url.IsLocalUrl(returnUrl) || AppUrlProvider.IsRedirectAllowedUrl(returnUrl))
if (Url.IsLocalUrl(returnUrl) || await AppUrlProvider.IsRedirectAllowedUrlAsync(returnUrl))
{
return returnUrl;
}
return GetAppHomeUrl();
return await GetAppHomeUrlAsync();
}
protected virtual string GetAppHomeUrl()
protected virtual Task<string> GetAppHomeUrlAsync()
{
return "~/"; //TODO: ???
return Task.FromResult("~/"); //TODO: ???
}
}

21
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
@ -104,14 +105,14 @@ public abstract class AbpController : Controller, IAvoidDuplicateCrossCuttingCon
return localizer;
}
protected virtual RedirectResult RedirectSafely(string returnUrl, string? returnUrlHash = null)
protected virtual async Task<RedirectResult> RedirectSafelyAsync(string returnUrl, string? returnUrlHash = null)
{
return Redirect(GetRedirectUrl(returnUrl, returnUrlHash));
return Redirect(await GetRedirectUrlAsync(returnUrl, returnUrlHash));
}
protected virtual string GetRedirectUrl(string returnUrl, string? returnUrlHash = null)
protected virtual async Task<string> GetRedirectUrlAsync(string returnUrl, string? returnUrlHash = null)
{
returnUrl = NormalizeReturnUrl(returnUrl);
returnUrl = await NormalizeReturnUrlAsync(returnUrl);
if (!returnUrlHash.IsNullOrWhiteSpace())
{
@ -121,23 +122,23 @@ public abstract class AbpController : Controller, IAvoidDuplicateCrossCuttingCon
return returnUrl;
}
protected virtual string NormalizeReturnUrl(string returnUrl)
protected virtual async Task<string> NormalizeReturnUrlAsync(string returnUrl)
{
if (returnUrl.IsNullOrEmpty())
{
return GetAppHomeUrl();
return await GetAppHomeUrlAsync();
}
if (Url.IsLocalUrl(returnUrl) || AppUrlProvider.IsRedirectAllowedUrl(returnUrl))
if (Url.IsLocalUrl(returnUrl) || await AppUrlProvider.IsRedirectAllowedUrlAsync(returnUrl))
{
return returnUrl;
}
return GetAppHomeUrl();
return await GetAppHomeUrlAsync();
}
protected virtual string GetAppHomeUrl()
protected virtual Task<string> GetAppHomeUrlAsync()
{
return Url.Content("~/");
return Task.FromResult(Url.Content("~/"));
}
}

22
framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs

@ -22,38 +22,38 @@ public abstract class ChallengeAccountController : AbpController
}
[HttpGet]
public virtual ActionResult Login(string returnUrl = "", string returnUrlHash = "")
public virtual async Task<ActionResult> LoginAsync(string returnUrl = "", string returnUrlHash = "")
{
if (CurrentUser.IsAuthenticated)
{
return RedirectSafely(returnUrl, returnUrlHash);
return await RedirectSafelyAsync(returnUrl, returnUrlHash);
}
return Challenge(new AuthenticationProperties { RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
return Challenge(new AuthenticationProperties { RedirectUri = await GetRedirectUrlAsync(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
}
[HttpGet]
public virtual async Task<ActionResult> Logout(string returnUrl = "", string returnUrlHash = "")
public virtual async Task<ActionResult> LogoutAsync(string returnUrl = "", string returnUrlHash = "")
{
await HttpContext.SignOutAsync();
if (HttpContext.User.Identity?.AuthenticationType == AuthenticationType)
{
return RedirectSafely(returnUrl, returnUrlHash);
return await RedirectSafelyAsync(returnUrl, returnUrlHash);
}
return SignOut(new AuthenticationProperties { RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
return SignOut(new AuthenticationProperties { RedirectUri = await GetRedirectUrlAsync(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
}
[HttpGet]
public virtual async Task<IActionResult> FrontChannelLogout(string sid)
public virtual async Task<IActionResult> FrontChannelLogoutAsync(string sid)
{
if (User.Identity != null && User.Identity.IsAuthenticated)
{
var currentSid = User.FindFirst("sid")?.Value ?? string.Empty;
if (string.Equals(currentSid, sid, StringComparison.Ordinal))
{
await Logout();
await LogoutAsync();
}
}
@ -61,7 +61,7 @@ public abstract class ChallengeAccountController : AbpController
}
[HttpGet]
public virtual Task<IActionResult> AccessDenied()
public virtual Task<IActionResult> AccessDeniedAsync()
{
return Task.FromResult<IActionResult>(Challenge(
new AuthenticationProperties
@ -78,9 +78,9 @@ public abstract class ChallengeAccountController : AbpController
}
[HttpGet]
public virtual async Task<ActionResult> Challenge(string returnUrl = "", string returnUrlHash = "")
public virtual async Task<ActionResult> ChallengeAsync(string returnUrl = "", string returnUrlHash = "")
{
await HttpContext.SignOutAsync();
return Challenge(new AuthenticationProperties { RedirectUri = GetRedirectUrl(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
return Challenge(new AuthenticationProperties { RedirectUri = await GetRedirectUrlAsync(returnUrl, returnUrlHash) }, ChallengeAuthenticationSchemas);
}
}

10
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs

@ -15,7 +15,6 @@ public class AppUrlProvider : IAppUrlProvider, ITransientDependency
{
protected AppUrlOptions Options { get; }
protected IMultiTenantUrlProvider MultiTenantUrlProvider { get; }
public ILogger<AppUrlProvider> Logger { get; set; }
public AppUrlProvider(
@ -37,9 +36,14 @@ public class AppUrlProvider : IAppUrlProvider, ITransientDependency
);
}
public bool IsRedirectAllowedUrl(string url)
public virtual async Task<bool> IsRedirectAllowedUrlAsync(string url)
{
var allow = Options.RedirectAllowedUrls.Any(x => url.StartsWith(x, StringComparison.CurrentCultureIgnoreCase));
var redirectAllowedUrls = new List<string>();
foreach (var redirectAllowedUrl in Options.RedirectAllowedUrls)
{
redirectAllowedUrls.Add((await NormalizeUrlAsync(redirectAllowedUrl))!);
}
var allow = redirectAllowedUrls.Any(x => url.StartsWith(x, StringComparison.CurrentCultureIgnoreCase));
if (!allow)
{
Logger.LogError($"Invalid RedirectUrl: {url}, Use {nameof(AppUrlProvider)} to configure it!");

4
framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs

@ -9,7 +9,7 @@ public interface IAppUrlProvider
Task<string?> GetUrlOrNullAsync([NotNull] string appName, string? urlName = null);
bool IsRedirectAllowedUrl(string url);
Task<bool> IsRedirectAllowedUrlAsync(string url);
Task<string?> NormalizeUrlAsync(string? url);
}

46
framework/test/Volo.Abp.UI.Navigation.Tests/Volo/Abp/Ui/Navigation/AppUrlProvider_Tests.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.ConfigurationStore;
using Volo.Abp.Testing;
using Volo.Abp.UI.Navigation.Urls;
using Xunit;
@ -15,6 +16,8 @@ public class AppUrlProvider_Tests : AbpIntegratedTest<AbpUiNavigationTestModule>
private readonly IAppUrlProvider _appUrlProvider;
private readonly ICurrentTenant _currentTenant;
private readonly Guid _tenantAId = Guid.NewGuid();
public AppUrlProvider_Tests()
{
_appUrlProvider = ServiceProvider.GetRequiredService<AppUrlProvider>();
@ -35,12 +38,22 @@ public class AppUrlProvider_Tests : AbpIntegratedTest<AbpUiNavigationTestModule>
options.RedirectAllowedUrls.AddRange(new List<string>()
{
"https://wwww.volosoft.com",
"https://wwww.aspnetzero.com"
"https://wwww.aspnetzero.com",
"https://{{tenantName}}.abp.io",
"https://{{tenantId}}.abp.io"
});
options.Applications["BLAZOR"].RootUrl = "https://{{tenantId}}.abp.io";
options.Applications["BLAZOR"].Urls["PasswordReset"] = "account/reset-password";
});
services.Configure<AbpDefaultTenantStoreOptions>(options =>
{
options.Tenants = new TenantConfiguration[]
{
new(_tenantAId, "community")
};
});
}
[Fact]
@ -64,14 +77,13 @@ public class AppUrlProvider_Tests : AbpIntegratedTest<AbpUiNavigationTestModule>
url.ShouldBe("https://community.abp.io/account/reset-password");
}
var tenantId = Guid.NewGuid();
using (_currentTenant.Change(tenantId))
using (_currentTenant.Change(_tenantAId))
{
var url = await _appUrlProvider.GetUrlAsync("BLAZOR");
url.ShouldBe($"https://{tenantId}.abp.io");
url.ShouldBe($"https://{_tenantAId}.abp.io");
url = await _appUrlProvider.GetUrlAsync("BLAZOR", "PasswordReset");
url.ShouldBe($"https://{tenantId}.abp.io/account/reset-password");
url.ShouldBe($"https://{_tenantAId}.abp.io/account/reset-password");
}
await Assert.ThrowsAsync<AbpException>(async () =>
@ -87,9 +99,27 @@ public class AppUrlProvider_Tests : AbpIntegratedTest<AbpUiNavigationTestModule>
}
[Fact]
public void IsRedirectAllowedUrl()
public async Task IsRedirectAllowedUrlAsync()
{
_appUrlProvider.IsRedirectAllowedUrl("https://community.abp.io").ShouldBeFalse();
_appUrlProvider.IsRedirectAllowedUrl("https://wwww.volosoft.com").ShouldBeTrue();
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://community.abp.io")).ShouldBeFalse();
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://wwww.volosoft.com")).ShouldBeTrue();
using (_currentTenant.Change(null))
{
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://www.abp.io")).ShouldBeFalse();
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://abp.io")).ShouldBeTrue();
}
using (_currentTenant.Change(_tenantAId, "community"))
{
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://community.abp.io")).ShouldBeTrue();
(await _appUrlProvider.IsRedirectAllowedUrlAsync("https://community2.abp.io")).ShouldBeFalse();
}
using (_currentTenant.Change(_tenantAId))
{
(await _appUrlProvider.IsRedirectAllowedUrlAsync($"https://{_tenantAId}.abp.io")).ShouldBeTrue();
(await _appUrlProvider.IsRedirectAllowedUrlAsync($"https://{Guid.NewGuid()}.abp.io")).ShouldBeFalse();
}
}
}

2
modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs

@ -177,7 +177,7 @@ public class IdentityServerSupportedLoginModel : LoginModel
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(ReturnUrl, ReturnUrlHash);
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
public override async Task<IActionResult> OnPostExternalLogin(string provider)

18
modules/account/src/Volo.Abp.Account.Web/Pages/Account/LoggedOut.cshtml.cs

@ -18,28 +18,28 @@ public class LoggedOutModel : AccountPageModel
[BindProperty(SupportsGet = true)]
public string PostLogoutRedirectUri { get; set; }
public virtual Task<IActionResult> OnGetAsync()
public virtual async Task<IActionResult> OnGetAsync()
{
NormalizeUrl();
return Task.FromResult<IActionResult>(Page());
await NormalizeUrlAsync();
return Page();
}
public virtual Task<IActionResult> OnPostAsync()
public virtual async Task<IActionResult> OnPostAsync()
{
NormalizeUrl();
return Task.FromResult<IActionResult>(Page());
await NormalizeUrlAsync();
return Page();
}
protected virtual void NormalizeUrl()
protected virtual async Task NormalizeUrlAsync()
{
if (!PostLogoutRedirectUri.IsNullOrWhiteSpace())
{
PostLogoutRedirectUri = Url.Content(GetRedirectUrl(PostLogoutRedirectUri));
PostLogoutRedirectUri = Url.Content(await GetRedirectUrlAsync(PostLogoutRedirectUri));
}
if(!SignOutIframeUrl.IsNullOrWhiteSpace())
{
SignOutIframeUrl = Url.Content(GetRedirectUrl(SignOutIframeUrl));
SignOutIframeUrl = Url.Content(await GetRedirectUrlAsync(SignOutIframeUrl));
}
}
}

6
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs

@ -143,7 +143,7 @@ public class LoginModel : AccountPageModel
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(ReturnUrl, ReturnUrlHash);
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
/// <summary>
@ -237,7 +237,7 @@ public class LoginModel : AccountPageModel
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
}
return RedirectSafely(returnUrl, returnUrlHash);
return await RedirectSafelyAsync(returnUrl, returnUrlHash);
}
//TODO: Handle other cases for result!
@ -279,7 +279,7 @@ public class LoginModel : AccountPageModel
// Clear the dynamic claims cache.
await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);
return RedirectSafely(returnUrl, returnUrlHash);
return await RedirectSafelyAsync(returnUrl, returnUrlHash);
}
protected virtual async Task ReplaceEmailToUsernameOfInputIfNeeds()

2
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Logout.cshtml.cs

@ -27,7 +27,7 @@ public class LogoutModel : AccountPageModel
await SignInManager.SignOutAsync();
if (ReturnUrl != null)
{
return RedirectSafely(ReturnUrl, ReturnUrlHash);
return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}
if (await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin))

2
modules/account/src/Volo.Abp.Account.Web/Pages/Account/Manage.cshtml.cs

@ -38,7 +38,7 @@ public class ManageModel : AccountPageModel
{
if (!Url.IsLocalUrl(ReturnUrl) &&
!ReturnUrl.StartsWith(UriHelper.BuildAbsolute(Request.Scheme, Request.Host, Request.PathBase).RemovePostFix("/")) &&
!AppUrlProvider.IsRedirectAllowedUrl(ReturnUrl))
!await AppUrlProvider.IsRedirectAllowedUrlAsync(ReturnUrl))
{
ReturnUrl = null;
}

6
modules/account/src/Volo.Abp.Account.Web/Pages/Account/ResetPasswordConfirmation.cshtml.cs

@ -14,10 +14,10 @@ public class ResetPasswordConfirmationModel : AccountPageModel
[BindProperty(SupportsGet = true)]
public string ReturnUrlHash { get; set; }
public virtual Task<IActionResult> OnGetAsync()
public virtual async Task<IActionResult> OnGetAsync()
{
ReturnUrl = GetRedirectUrl(ReturnUrl, ReturnUrlHash);
ReturnUrl = await GetRedirectUrlAsync(ReturnUrl, ReturnUrlHash);
return Task.FromResult<IActionResult>(Page());
return Page();
}
}

Loading…
Cancel
Save