Add AbpStrictRedirectUriValidator_Tests

pull/7783/head
maliming 4 years ago
parent c20a5b370e
commit 17a24a2a7f

@ -6,11 +6,11 @@ namespace Volo.Abp.IdentityServer
{ {
public static class AbpIdentityServerServiceCollectionExtensions public static class AbpIdentityServerServiceCollectionExtensions
{ {
public static void AddAbpStrictRedirectUriValidator(this IServiceCollection services, string domainFormat) public static void AddAbpStrictRedirectUriValidator(this IServiceCollection services, params string[] domainFormats)
{ {
services.Configure<AbpRedirectUriValidatorOptions>(options => services.Configure<AbpRedirectUriValidatorOptions>(options =>
{ {
options.DomainFormat = domainFormat; options.DomainFormats.AddRange(domainFormats);
}); });
services.Replace(ServiceDescriptor.Transient<IRedirectUriValidator, AbpStrictRedirectUriValidator>()); services.Replace(ServiceDescriptor.Transient<IRedirectUriValidator, AbpStrictRedirectUriValidator>());

@ -1,7 +1,14 @@
namespace Volo.Abp.IdentityServer using System.Collections.Generic;
namespace Volo.Abp.IdentityServer
{ {
public class AbpRedirectUriValidatorOptions public class AbpRedirectUriValidatorOptions
{ {
public string DomainFormat { get; set; } public List<string> DomainFormats { get; }
public AbpRedirectUriValidatorOptions()
{
DomainFormats = new List<string>();
}
} }
} }

@ -1,5 +1,5 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Validation; using IdentityServer4.Validation;
@ -8,11 +8,7 @@ using Volo.Abp.Text.Formatting;
namespace Volo.Abp.IdentityServer namespace Volo.Abp.IdentityServer
{ {
/// <summary> public class AbpStrictRedirectUriValidator : StrictRedirectUriValidator
/// Default implementation of redirect URI validator. Validates the URIs against
/// the client's configured URIs.
/// </summary>
public class AbpStrictRedirectUriValidator : IRedirectUriValidator
{ {
protected AbpRedirectUriValidatorOptions Options { get; } protected AbpRedirectUriValidatorOptions Options { get; }
@ -21,63 +17,38 @@ namespace Volo.Abp.IdentityServer
Options = options.Value; Options = options.Value;
} }
/// <summary> public override async Task<bool> IsRedirectUriValidAsync(string requestedUri, Client client)
/// Checks if a given URI string is in a collection of strings (using ordinal ignore case comparison) {
/// </summary> var isAllowed = await base.IsRedirectUriValidAsync(requestedUri, client);
/// <param name="uris">The uris.</param> return isAllowed || await IsRedirectUriValidWithDomainFormatsAsync(client.RedirectUris, requestedUri);
/// <param name="requestedUri">The requested URI.</param> }
/// <returns></returns>
protected virtual bool StringCollectionContainsString(IEnumerable<string> uris, string requestedUri) public override async Task<bool> IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client)
{
var isAllowed = await base.IsPostLogoutRedirectUriValidAsync(requestedUri, client);
return isAllowed || await IsRedirectUriValidWithDomainFormatsAsync(client.PostLogoutRedirectUris, requestedUri);
}
protected virtual Task<bool> IsRedirectUriValidWithDomainFormatsAsync(IEnumerable<string> uris, string requestedUri)
{ {
if (uris == null) if (uris == null)
{ {
return false; return Task.FromResult(false);
} }
foreach (var url in uris) foreach (var url in uris)
{ {
if (url.Contains(requestedUri, StringComparison.OrdinalIgnoreCase)) if (Options.DomainFormats.Any(x => url.Contains(x)))
{
return true;
}
if (url.Contains(Options.DomainFormat))
{ {
var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true); var extractResult = FormattedStringValueExtracter.Extract(requestedUri, url, ignoreCase: true);
if (extractResult.IsMatch) if (extractResult.IsMatch)
{ {
return true; return Task.FromResult(true);
} }
} }
} }
return false; return Task.FromResult(false);
}
/// <summary>
/// Determines whether a redirect URI is valid for a client.
/// </summary>
/// <param name="requestedUri">The requested URI.</param>
/// <param name="client">The client.</param>
/// <returns>
/// <c>true</c> is the URI is valid; <c>false</c> otherwise.
/// </returns>
public virtual Task<bool> IsRedirectUriValidAsync(string requestedUri, Client client)
{
return Task.FromResult(StringCollectionContainsString(client.RedirectUris, requestedUri));
}
/// <summary>
/// Determines whether a post logout URI is valid for a client.
/// </summary>
/// <param name="requestedUri">The requested URI.</param>
/// <param name="client">The client.</param>
/// <returns>
/// <c>true</c> is the URI is valid; <c>false</c> otherwise.
/// </returns>
public virtual Task<bool> IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client)
{
return Task.FromResult(StringCollectionContainsString(client.PostLogoutRedirectUris, requestedUri));
} }
} }
} }

@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Xunit;
namespace Volo.Abp.IdentityServer
{
public class AbpStrictRedirectUriValidator_Tests : AbpIdentityServerTestBase
{
private readonly IRedirectUriValidator _abpStrictRedirectUriValidator;
private readonly Client _testClient = new Client
{
RedirectUris = new List<string>
{
"https://{0}.api.abp.io:8080/signin-oidc",
"http://{0}.ng.abp.io/index.html"
},
PostLogoutRedirectUris = new List<string>
{
"https://{0}.api.abp.io:8080/signin-oidc",
"http://{0}.ng.abp.io/index.html"
}
};
public AbpStrictRedirectUriValidator_Tests()
{
_abpStrictRedirectUriValidator = GetRequiredService<IRedirectUriValidator>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
services.AddAbpStrictRedirectUriValidator("{0}.api.abp.io:8080/signin-oidc", "{0}.ng.abp.io/index.html");
}
[Fact]
public void Should_Register_AbpStrictRedirectUriValidator()
{
_abpStrictRedirectUriValidator.GetType().ShouldBe(typeof(AbpStrictRedirectUriValidator));
}
[Fact]
public async Task IsRedirectUriValidAsync()
{
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://t1.api.abp.io:8080/signin-oidc", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://t2.ng.abp.io/index.html", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse();
}
[Fact]
public async Task IsPostLogoutRedirectUriValidAsync()
{
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://t1.api.abp.io:8080/signin-oidc", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://t2.ng.abp.io/index.html", _testClient)).ShouldBeTrue();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.abp.io", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("https://api.t1.abp:8080/", _testClient)).ShouldBeFalse();
(await _abpStrictRedirectUriValidator.IsPostLogoutRedirectUriValidAsync("http://ng.t1.abp.io", _testClient)).ShouldBeFalse();
}
}
}
Loading…
Cancel
Save