1

我正在使用 Visual Studio 2015 Enterprise Update 1 和 ASP.NET 5 rc1-final 来构建一个端点,该端点发出和使用 JWT 令牌,如这里详细描述的那样。

现在我开始进行单元测试,在测试 AspNet.Security.OpenIdConnect.Server (OIDC/ASOS) 的某些方面时遇到了摩擦。具体来说,一些 ASOS 原语,例如 LogoutEndpointContext 不是抽象的,因此它们不容易被模拟。通常我只会对这些类型进行假冒,但 DNX 似乎不支持假冒,至少现在还不支持。这些类型也是密封的,所以我不能专门化它们。

这迫使我编写一些脆弱的反射代码来构造这些密封的 ASOS 类型。这是一个需要 LogoutEndpointContext 的示例 XUnit 测试,因此我可以测试我的 OpenIdConnectServerProvider 事件处理(在这种情况下,非 POST 注销应该引发异常);注意我为了实例化 LogoutEndpointContext 必须做的反射:

[Fact]
async public Task API_Initialization_Services_AuthenticatedUser_Authentication_LogoutEndpoint_XSRF_Unit()
{
    // Arrange

        Mock<HttpRequest> mockRequest = new Mock<HttpRequest>();
        mockRequest.SetupGet(a => a.Method).Returns(() => "Not Post");
        Mock<HttpContext> mockContext = new Mock<HttpContext>();
        mockContext.SetupGet(a => a.Request).Returns(() => mockRequest.Object);

        OpenIdConnectServerOptions options = new OpenIdConnectServerOptions();

        OpenIdConnectMessage request = new OpenIdConnectMessage();

        // I would prefer not to use reflection
        var ctorInfo = typeof(LogoutEndpointContext).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).Single();

        LogoutEndpointContext context = (LogoutEndpointContext)ctorInfo.Invoke(new object[] {mockContext.Object,options, request});

    // Act

        AuthenticationEvents authenticationEvents = new AuthenticationEvents();

    // Assert

        await Assert.ThrowsAsync<SecurityTokenValidationException>(() => authenticationEvents.LogoutEndpoint(context));
}

任何关于如何更好地实例化/模拟/伪造/专门化像 LogoutEndpointContext 这样的密封 ASOS 类型的建议都将非常感激。

4

1 回答 1

0

在测试严重依赖 HTTP 上下文的中间件时,单元测试很少是最简单的选择。TestServer我们推荐的方法是通过设置内存管道来选择功能测试:

public class OAuthLogoutEndpointTests {
    [Fact]
    public async Task EnsureThatOnlyPostLogoutRequestsAreValid() {
        // Arrange
        var server = CreateAuthorizationServer();
        var client = server.CreateClient();

        var request = new HttpRequestMessage(HttpMethod.Get, "/connect/logout");

        // Act
        var response = await client.SendAsync(request);

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
    }

    private static TestServer CreateAuthorizationServer(Action<OpenIdConnectServerOptions> configuration = null) {
        var builder = new WebHostBuilder();

        builder.UseEnvironment("Testing");

        builder.ConfigureServices(services => {
            services.AddAuthentication();
            services.AddCaching();
            services.AddLogging();
        });

        builder.Configure(app => {
            // Add a new OpenID Connect server instance.
            app.UseOpenIdConnectServer(options => {
                // Use your own provider:
                // options.Provider = new AuthenticationEvents();

                options.Provider = new OpenIdConnectServerProvider {
                    OnValidateLogoutRequest = context => {
                        // Reject non-POST logout requests.
                        if (!string.Equals(context.HttpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) {
                            context.Reject(
                                error: OpenIdConnectConstants.Errors.InvalidRequest,
                                description: "Only POST requests are supported.");
                        }

                        return Task.FromResult(0);
                    }
                };

                // Run the configuration delegate
                // registered by the unit tests.
                configuration?.Invoke(options);
            });
        });

        return new TestServer(builder);
    }
}

(您可以在这里找到更多测试:https ://github.com/aspnet-contrib/AspNet.Security.OAuth.Extensions/blob/master/test/AspNet.Security.OAuth.Introspection.Tests/OAuthIntrospectionMiddlewareTests.cs )

编辑:此答案的其余部分不再适用,因为上下文类已在 ASOS beta5 中解封。


在 ASOS 中,所有的上下文类都被密封了,它们的构造函数被标记为内部的,以防止开发人员实例化它们并在生产代码中手动调用提供者。也就是说,如果您强烈认为我们不应该这样做,请随时在 GitHub 存储库上打开一张新票。

最后一点:如果您希望您的 OIDC 服务器符合规范,那么拒绝 GET 请求可能不是一个好主意,因为它是规范中明确提到的唯一动词。

于 2016-01-04T19:14:32.780 回答