2026-04-19 16:02:50 +01:00
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
|
|
|
|
using System.Net.Security;
|
2025-07-30 15:52:01 +01:00
|
|
|
|
using IdentityModel.Client;
|
2026-07-12 03:43:22 +01:00
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2018-07-15 00:29:06 +02:00
|
|
|
|
|
2026-06-19 19:59:15 +01:00
|
|
|
|
namespace Yavsc.Org.Tests
|
2018-07-15 00:29:06 +02:00
|
|
|
|
{
|
2025-07-16 00:47:50 +01:00
|
|
|
|
[Collection("Yavsc Server")]
|
2021-01-04 00:11:27 +00:00
|
|
|
|
[Trait("regression", "oui")]
|
2025-07-13 18:13:04 +01:00
|
|
|
|
public class Remoting : BaseTestContext, IClassFixture<WebServerFixture>
|
2018-07-15 00:29:06 +02:00
|
|
|
|
{
|
2025-07-13 18:13:04 +01:00
|
|
|
|
public Remoting(WebServerFixture serverFixture, ITestOutputHelper output)
|
2018-08-02 12:28:13 +02:00
|
|
|
|
: base(output, serverFixture)
|
2018-07-21 16:15:54 +02:00
|
|
|
|
{
|
2025-07-13 18:13:04 +01:00
|
|
|
|
|
2018-07-21 16:15:54 +02:00
|
|
|
|
}
|
2018-07-15 00:29:06 +02:00
|
|
|
|
|
2025-07-30 15:52:01 +01:00
|
|
|
|
|
|
|
|
|
|
[Fact]
|
|
|
|
|
|
public async Task ObtainServiceToken()
|
2026-04-19 16:02:50 +01:00
|
|
|
|
{
|
|
|
|
|
|
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
|
|
|
|
|
if (string.IsNullOrEmpty(serverUrl))
|
|
|
|
|
|
throw new InvalidOperationException("No HTTPS server address found");
|
2026-04-19 17:15:10 +01:00
|
|
|
|
|
2026-04-19 16:02:50 +01:00
|
|
|
|
HttpClient client = NewHttpClient();
|
|
|
|
|
|
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
2026-07-12 03:43:22 +01:00
|
|
|
|
if (disco.IsError)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Diagnostic 2026-07-12 : capture the raw HTTP response
|
|
|
|
|
|
// AND dump the OIDC-related DB state so we can pinpoint
|
|
|
|
|
|
// which state is corrupt when the discovery is broken.
|
|
|
|
|
|
var rawResp = await client.GetAsync(serverUrl + "/.well-known/openid-configuration");
|
|
|
|
|
|
var body = await rawResp.Content.ReadAsStringAsync();
|
|
|
|
|
|
|
|
|
|
|
|
string dbState = "no logger";
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
using var scope = _serverFixture.Services.CreateScope();
|
|
|
|
|
|
var cfg = scope.ServiceProvider
|
|
|
|
|
|
.GetRequiredService<IdentityServer8.EntityFramework.DbContexts.ConfigurationDbContext>();
|
|
|
|
|
|
var clients = cfg.Clients.Select(c => new {
|
|
|
|
|
|
c.Id, c.ClientId, c.Enabled, c.RequireClientSecret
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
var apiScopes = cfg.ApiScopes.Select(s => new { s.Name, s.Enabled }).ToList();
|
|
|
|
|
|
var apiResources = cfg.ApiResources.Select(r => new { r.Name, r.Enabled }).ToList();
|
|
|
|
|
|
var identityResources = cfg.IdentityResources.Select(r => new { r.Name, r.Enabled }).ToList();
|
|
|
|
|
|
dbState = $"clients={System.Text.Json.JsonSerializer.Serialize(clients)}\n" +
|
|
|
|
|
|
$"apiScopes={System.Text.Json.JsonSerializer.Serialize(apiScopes)}\n" +
|
|
|
|
|
|
$"apiResources={System.Text.Json.JsonSerializer.Serialize(apiResources)}\n" +
|
|
|
|
|
|
$"identityResources={System.Text.Json.JsonSerializer.Serialize(identityResources)}";
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception dumpEx)
|
|
|
|
|
|
{
|
|
|
|
|
|
dbState = $"dump failed: {dumpEx.Message}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
throw new Exception(
|
|
|
|
|
|
$"disco.Error={disco.Error}\n" +
|
|
|
|
|
|
$"HTTP status={(int)rawResp.StatusCode}\n" +
|
|
|
|
|
|
$"Body[0..2000]:\n{body.Substring(0, Math.Min(2000, body.Length))}\n" +
|
|
|
|
|
|
$"---\n" +
|
|
|
|
|
|
$"OIDC DB state at failure:\n{dbState}");
|
|
|
|
|
|
}
|
2025-07-15 19:43:41 +01:00
|
|
|
|
|
2026-04-19 16:02:50 +01:00
|
|
|
|
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
|
|
|
|
|
|
{
|
|
|
|
|
|
Address = disco.TokenEndpoint,
|
|
|
|
|
|
ClientId = _serverFixture.TestClientId,
|
|
|
|
|
|
ClientSecret = _serverFixture.TestClientSecret,
|
|
|
|
|
|
Scope = "test",
|
|
|
|
|
|
GrantType = "client_credentials"
|
|
|
|
|
|
});
|
|
|
|
|
|
if (response.IsError) throw new Exception(response.Error);
|
|
|
|
|
|
}
|
2025-07-15 17:35:14 +01:00
|
|
|
|
|
2026-04-19 17:15:10 +01:00
|
|
|
|
private static HttpClient NewHttpClient()
|
|
|
|
|
|
{
|
|
|
|
|
|
return new HttpClient(new BypassSslValidationHandler());
|
|
|
|
|
|
}
|
2025-07-16 00:47:50 +01:00
|
|
|
|
|
2026-04-19 16:02:50 +01:00
|
|
|
|
[Fact]
|
2025-07-30 15:52:01 +01:00
|
|
|
|
public async Task ObtainResourceOwnerPasswordToken()
|
|
|
|
|
|
{
|
2026-04-19 16:02:50 +01:00
|
|
|
|
var serverUrl = _serverFixture.Addresses.FirstOrDefault(u => u.StartsWith("https:"));
|
|
|
|
|
|
if (string.IsNullOrEmpty(serverUrl))
|
|
|
|
|
|
throw new InvalidOperationException("No HTTPS server address found");
|
2026-04-19 17:15:10 +01:00
|
|
|
|
|
2026-03-09 02:07:09 +00:00
|
|
|
|
var client = NewHttpClient();
|
2026-04-19 16:02:50 +01:00
|
|
|
|
var disco = await client.GetDiscoveryDocumentAsync(serverUrl);
|
2025-07-30 15:52:01 +01:00
|
|
|
|
if (disco.IsError) throw new Exception(disco.Error);
|
2018-07-15 00:29:06 +02:00
|
|
|
|
|
2025-07-30 15:52:01 +01:00
|
|
|
|
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
|
2026-04-19 16:02:50 +01:00
|
|
|
|
{
|
|
|
|
|
|
Address = disco.TokenEndpoint,
|
|
|
|
|
|
ClientId = _serverFixture.TestClientId,
|
|
|
|
|
|
ClientSecret = _serverFixture.TestClientSecret,
|
|
|
|
|
|
UserName = _serverFixture.TestingUserName,
|
|
|
|
|
|
Password = _serverFixture.TestingUserPassword,
|
|
|
|
|
|
Scope = "test",
|
|
|
|
|
|
Parameters =
|
2018-08-02 12:28:13 +02:00
|
|
|
|
{
|
2025-07-30 15:52:01 +01:00
|
|
|
|
{ "acr_values", "tenant:custom_account_store1 foo bar quux" }
|
|
|
|
|
|
}
|
2026-04-19 16:02:50 +01:00
|
|
|
|
});
|
2025-07-30 15:52:01 +01:00
|
|
|
|
|
|
|
|
|
|
if (response.IsError) throw new Exception(response.Error);
|
|
|
|
|
|
|
2018-07-15 00:29:06 +02:00
|
|
|
|
}
|
2018-08-02 12:28:13 +02:00
|
|
|
|
|
2025-07-16 00:47:50 +01:00
|
|
|
|
public static IEnumerable<object[]> GetLoginIntentData()
|
2025-07-13 18:13:04 +01:00
|
|
|
|
{
|
2025-07-16 00:47:50 +01:00
|
|
|
|
return new object[][] { new object[] { "testuser", "test" } };
|
2025-07-13 18:13:04 +01:00
|
|
|
|
}
|
2025-07-30 15:52:01 +01:00
|
|
|
|
|
2018-07-15 00:29:06 +02:00
|
|
|
|
}
|
2026-03-09 02:07:09 +00:00
|
|
|
|
|
2026-04-19 17:15:10 +01:00
|
|
|
|
internal class BypassSslValidationHandler : HttpClientHandler
|
2026-03-09 02:07:09 +00:00
|
|
|
|
{
|
2026-04-19 17:15:10 +01:00
|
|
|
|
public BypassSslValidationHandler()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Override validation for this handler only
|
|
|
|
|
|
ServerCertificateCustomValidationCallback = ValidateCertificate;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool ValidateCertificate(
|
|
|
|
|
|
HttpRequestMessage request,
|
|
|
|
|
|
X509Certificate2? certificate,
|
|
|
|
|
|
X509Chain? chain,
|
|
|
|
|
|
SslPolicyErrors errors)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Accept all certificates (bypass validation)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2026-03-09 02:07:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|