Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 24ba609e0c | |||
| 4bac14881d | |||
| 29f6d66c44 | |||
| fd6057d691 | |||
| bcfc9ef962 | |||
| 7961d934ba |
@@ -362,3 +362,4 @@ MigrationBackup/
|
|||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
/LiteCharms.Features.Tests/http/http-client.env.json
|
/LiteCharms.Features.Tests/http/http-client.env.json
|
||||||
|
/LiteCharms.Features.Tests/http/midrandshop-api/http-client.env.json
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public sealed class PayfastServiceFeatureTests(Fixture fixture) : IClassFixture<
|
|||||||
|
|
||||||
string liveTargetIp = addresses.First().ToString();
|
string liveTargetIp = addresses.First().ToString();
|
||||||
|
|
||||||
var result = await payfastService.ValidateReferrerIpAsync(liveTargetIp, fixture.CancellationToken);
|
var result = await payfastService.ValidateReferrerIpAsync(liveTargetIp, true, fixture.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.IsSuccess);
|
Assert.True(result.IsSuccess);
|
||||||
Assert.True(result.Value);
|
Assert.True(result.Value);
|
||||||
@@ -48,7 +48,7 @@ public sealed class PayfastServiceFeatureTests(Fixture fixture) : IClassFixture<
|
|||||||
{
|
{
|
||||||
string rogueIp = "8.8.8.8";
|
string rogueIp = "8.8.8.8";
|
||||||
|
|
||||||
var result = await payfastService.ValidateReferrerIpAsync(rogueIp, fixture.CancellationToken);
|
var result = await payfastService.ValidateReferrerIpAsync(rogueIp, true, fixture.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.IsSuccess);
|
Assert.True(result.IsSuccess);
|
||||||
Assert.False(result.Value);
|
Assert.False(result.Value);
|
||||||
@@ -87,7 +87,7 @@ public sealed class PayfastServiceFeatureTests(Fixture fixture) : IClassFixture<
|
|||||||
|
|
||||||
var result = await payfastService.ValidateServerConfirmationAsync(arbitraryParameters, isSandbox: true, fixture.CancellationToken);
|
var result = await payfastService.ValidateServerConfirmationAsync(arbitraryParameters, isSandbox: true, fixture.CancellationToken);
|
||||||
|
|
||||||
Assert.True(result.IsSuccess);
|
Assert.True(result.IsSuccess);
|
||||||
Assert.False(result.Value); // Handshake data rejected as fraudulent/unrecognized
|
Assert.False(result.Value); // Handshake data rejected as fraudulent/unrecognized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -61,7 +61,7 @@ public sealed class PayfastPaymentConfirmationReceivedEventHandler(IServiceProvi
|
|||||||
|
|
||||||
if (notification.PerformBackgroundChecks)
|
if (notification.PerformBackgroundChecks)
|
||||||
{
|
{
|
||||||
var isHostValid = await payfastService.ValidateReferrerIpAsync(notification.RemoteIpAddress!, cancellationToken);
|
var isHostValid = await payfastService.ValidateReferrerIpAsync(notification.RemoteIpAddress!, notification.AllowLoopback, cancellationToken);
|
||||||
|
|
||||||
if (isHostValid.IsFailed)
|
if (isHostValid.IsFailed)
|
||||||
throw new Exception("Security validation exception: Webhook packet source address failed cluster validation checks.");
|
throw new Exception("Security validation exception: Webhook packet source address failed cluster validation checks.");
|
||||||
|
|||||||
+6
-3
@@ -13,15 +13,18 @@ public sealed class PayfastPaymentConfirmationReceivedEvent : EventBase, IEvent
|
|||||||
|
|
||||||
public bool PerformBackgroundChecks { get; set; }
|
public bool PerformBackgroundChecks { get; set; }
|
||||||
|
|
||||||
|
public bool AllowLoopback { get; set; }
|
||||||
|
|
||||||
public PayfastPaymentConfirmationReceivedEvent() { }
|
public PayfastPaymentConfirmationReceivedEvent() { }
|
||||||
|
|
||||||
private PayfastPaymentConfirmationReceivedEvent(PayfastWebhookPayload? payload, string paymentId, bool performBackgroundChecks = true)
|
private PayfastPaymentConfirmationReceivedEvent(PayfastWebhookPayload? payload, string paymentId, bool performBackgroundChecks = true, bool allowLoopback = false)
|
||||||
{
|
{
|
||||||
Payload = payload;
|
Payload = payload;
|
||||||
CorrelationId = paymentId;
|
CorrelationId = paymentId;
|
||||||
PerformBackgroundChecks = performBackgroundChecks;
|
PerformBackgroundChecks = performBackgroundChecks;
|
||||||
|
AllowLoopback = allowLoopback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PayfastPaymentConfirmationReceivedEvent Create(PayfastWebhookPayload? payload, string paymentId, bool performBackgroundChecks = true) =>
|
public static PayfastPaymentConfirmationReceivedEvent Create(PayfastWebhookPayload? payload, string paymentId, bool performBackgroundChecks = true, bool allowLoopback = false) =>
|
||||||
new(payload, paymentId, performBackgroundChecks);
|
new(payload, paymentId, performBackgroundChecks, allowLoopback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<Result<bool>> ValidateReferrerIpAsync(string remoteIpAddress, CancellationToken cancellationToken = default)
|
public async ValueTask<Result<bool>> ValidateReferrerIpAsync(string remoteIpAddress, bool allowLoopback = false, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(remoteIpAddress))
|
if (string.IsNullOrWhiteSpace(remoteIpAddress))
|
||||||
return Result.Fail<bool>("Remote IP address is null or whitespace.");
|
return Result.Fail<bool>("Remote IP address is null or whitespace.");
|
||||||
@@ -74,6 +74,12 @@ public sealed partial class PayfastService(IDbContextFactory<MidrandBooksDbConte
|
|||||||
|
|
||||||
if (IPAddress.TryParse(remoteIpAddress, out var incomingIp))
|
if (IPAddress.TryParse(remoteIpAddress, out var incomingIp))
|
||||||
{
|
{
|
||||||
|
if (allowLoopback && IPAddress.IsLoopback(incomingIp))
|
||||||
|
{
|
||||||
|
logger.LogInformation("Local development loopback IP '{RemoteIp}' allowed bypassing DNS verification.", remoteIpAddress);
|
||||||
|
return Result.Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
bool isValid = validIps.Contains(incomingIp);
|
bool isValid = validIps.Contains(incomingIp);
|
||||||
|
|
||||||
if (!isValid)
|
if (!isValid)
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"local": {
|
|
||||||
"baseUrl": "https://localhost:7196",
|
|
||||||
"paymentId": "jdPB2zaKM3Z",
|
|
||||||
"signature": "6aeff59bb74f2448ff2c3d81b2ec95de",
|
|
||||||
"item_name": "System Architecture Book",
|
|
||||||
"amount": "350.00"
|
|
||||||
},
|
|
||||||
"uat": {
|
|
||||||
"baseUrl": "https://api.uat.midrandbooks.co.za",
|
|
||||||
"paymentId": "jdPB2zaKM3Z",
|
|
||||||
"signature": "6aeff59bb74f2448ff2c3d81b2ec95de",
|
|
||||||
"item_name": "System Architecture Book",
|
|
||||||
"amount": "350.00"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user