public async Task AuthorizedRoute_WithSharedAccessKey_RegisteredWithSecretProvider_ShouldNotFailWithUnauthorized(Type secretProviderType, string headerName) { // Arrange string secretValue = $"secret-{Guid.NewGuid()}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddSingleton(secretProviderType, new InMemorySecretProvider(SecretName, secretValue))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { // Act var request = HttpRequestBuilder .Get(SharedAccessKeyAuthenticationController.AuthorizedGetRouteHeader) .WithHeader(headerName, secretValue); using (HttpResponseMessage response = await server.SendAsync(request)) { // Act Assert.NotEqual(HttpStatusCode.Unauthorized, response.StatusCode); } } }
public async Task AuthorizedRoute_WithSharedAccessKeyOnQueryParameter_ShouldFailWithUnauthorized(string parameterName, string parameterValue) { // Arrange string secretValue = $"secret-{Guid.NewGuid()}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddSecretStore(stores => stores.AddInMemory(SecretName, secretValue))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(SharedAccessKeyAuthenticationController.AuthorizedGetRouteQueryString) .WithParameter(parameterName, parameterValue); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } }
public async Task AuthorizedRoute_WithSharedAccessKey_ShouldFailWithUnauthorized_WhenAnyHeaderValue() { // Arrange string secretValue = $"secret-{Guid.NewGuid()}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddSecretStore(stores => stores.AddInMemory(SecretName, secretValue))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(SharedAccessKeyAuthenticationController.AuthorizedGetRouteHeader) .WithHeader(HeaderName, $"{secretValue};second header value other than {nameof(secretValue)}"); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } }
public async Task GetHealthWithCorrectBearerToken_WithIncorrectAzureManagedIdentityAuthorization_ReturnsUnauthorized() { // Arrange string issuer = _bogusGenerator.Internet.Url(); string authority = _bogusGenerator.Internet.Url(); string privateKey = GenerateRandomPrivateKey(); await using (var testOpenIdServer = await TestOpenIdServer.StartNewAsync(_logger)) { var validationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, ValidateIssuerSigningKey = true, ValidateLifetime = true }; var reader = new JwtTokenReader(validationParameters, testOpenIdServer.OpenIdAddressConfiguration); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => opt.Filters.AddJwtTokenAuthorization(jwt => jwt.JwtTokenReader = reader)); }); await using (var testApiServer = await TestApiServer.StartNewAsync(options, _logger)) { string accessToken = testOpenIdServer.RequestSecretToken(issuer, authority, privateKey, daysValid: 7); var request = HttpRequestBuilder .Get(HealthController.GetRoute) .WithHeader(JwtTokenAuthorizationOptions.DefaultHeaderName, accessToken); // Act using (HttpResponseMessage response = await testApiServer.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task CertificateAuthorizedRoute_EmitsSecurityEventsWhenRequested_RunsAuthentication() { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithSubject("unrecognized-subject-name")) { var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.SecretProvider, SubjectKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(SubjectKey, "CN=subject")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRouteEmitSecurityEvents); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.Contains(logEvents, logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); }); } } } }
public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailWithUnauthorized_WhenClientCertificateThumbprintDoesntMatch( string thumbprintNoise, bool expected) { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.Create()) { const string thumbprintKey = "thumbprint"; var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithThumbprint(X509ValidationLocation.SecretProvider, thumbprintKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(thumbprintKey, clientCertificate.Thumbprint + thumbprintNoise)) .AddSingleton(certificateValidator) .AddClientCertificate(clientCertificate) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(NoneAuthenticationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.True( (HttpStatusCode.Unauthorized == response.StatusCode) == expected, $"Response HTTP status code {(expected ? "should" : "shouldn't")} be 'Unauthorized' but was '{response.StatusCode}'"); } } } }
public async Task SendRequest_WithVersionTrackingOnCustomHeaderName_AddsApplicationVersionToResponse() { // Arrange string headerName = $"header-name-{Guid.NewGuid()}"; string expected = $"version-{Guid.NewGuid()}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddAppVersion(provider => new StubAppVersion(expected))) .Configure(app => app.UseVersionTracking(opt => opt.HeaderName = headerName)); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(HealthController.GetRoute); using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.False(response.Headers.Contains(DefaultHeaderName)); Assert.True(response.Headers.TryGetValues(headerName, out IEnumerable <string> values)); Assert.Equal(expected, Assert.Single(values)); } } }
public async Task SendRequest_WithSerilogCorrelationenrichment_ReturnsOkWithDifferentOperationIdAndSameTransactionId() { // Arrange var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation()) .PreConfigure(app => app.UseHttpCorrelation()) .ConfigureHost(host => host.UseSerilog((context, serviceProvider, config) => config.Enrich.WithHttpCorrelationInfo(serviceProvider) .WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var firstRequest = HttpRequestBuilder.Get(CorrelationController.GetRoute); using (HttpResponseMessage firstResponse = await server.SendAsync(firstRequest)) { // Assert Assert.Equal(HttpStatusCode.OK, firstResponse.StatusCode); CorrelationInfo firstCorrelationInfo = await AssertAppCorrelationInfoAsync(firstResponse); AssertLoggedCorrelationProperties(spySink, firstCorrelationInfo); var secondRequest = HttpRequestBuilder .Get(CorrelationController.GetRoute) .WithHeader("X-Transaction-ID", firstCorrelationInfo.TransactionId); using (HttpResponseMessage secondResponse = await server.SendAsync(secondRequest)) { Assert.Equal(HttpStatusCode.OK, secondResponse.StatusCode); CorrelationInfo secondCorrelationInfo = await AssertAppCorrelationInfoAsync(secondResponse); AssertLoggedCorrelationProperties(spySink, secondCorrelationInfo); Assert.NotEqual(firstCorrelationInfo.OperationId, secondCorrelationInfo.OperationId); Assert.Equal(firstCorrelationInfo.TransactionId, secondCorrelationInfo.TransactionId); } } } }
public async Task AddJwtBearer_WithoutBearerScheme_Fails() { // Arrange const string secretName = "JwtSigningKey"; string secretValue = BogusGenerator.Random.AlphaNumeric(25); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddSecretStore(stores => stores.AddInMemory(secretName, secretValue)) .AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer((jwt, serviceProvider) => { var secretProvider = serviceProvider.GetRequiredService <ISecretProvider>(); string key = secretProvider.GetRawSecretAsync(secretName).GetAwaiter().GetResult(); jwt.TokenValidationParameters = CreateTokenValidationParametersForSecret(key); }); }) .Configure(app => app.UseAuthentication() .UseAuthorization()); string tokenText = CreateBearerTokenFromSecret(secretValue); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(AuthorizedController.GetRoute) .WithHeader("Authorization", tokenText); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); AssertPostConfigureJwtOptions(server.ServiceProvider); } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationOnAppServiceHeader_ShouldSucceeds_WhenClientCertificateSubjectNameMatches(string actualSubject) { // Arrange const string expectedSubject = "known-subject"; using (var cert = SelfSignedCertificate.CreateWithSubject(expectedSubject)) { var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.SecretProvider, SubjectKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(SubjectKey, $"CN={actualSubject}")) .AddSingleton(certificateValidator); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { string clientCertificate = Convert.ToBase64String(cert.RawData); var request = HttpRequestBuilder .Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute) .WithHeader("X-ARR-ClientCert", clientCertificate); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Arrange bool equalSubject = expectedSubject == actualSubject; bool isUnauthorized = response.StatusCode == HttpStatusCode.Unauthorized; Assert.True(equalSubject != isUnauthorized, "Client certificate with the same subject name should result in an OK HTTP status code"); } } } }
public async Task SharedAccessKeyAuthorizedRoute_DoesntEmitSecurityEventsByDefault_RunsAuthentication() { // Arrange const string issuerKey = "issuer"; var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=issuer")) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(NoneAuthenticationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.DoesNotContain(logEvents, logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); }); } } }
public async Task GetHealthWithCorrectBearerToken_WithIncorrectAzureManagedIdentityAuthorization_ReturnsUnauthorized() { // Arrange string issuer = $"http://{Util.GetRandomString(10).ToLower()}.com"; string authority = $"http://{Util.GetRandomString(10).ToLower()}.com"; RSA rsa = new RSACryptoServiceProvider(512); string privateKey = rsa.ToCustomXmlString(true); using (var testServer = new TestApiServer()) using (var testOpenIdServer = await TestOpenIdServer.StartNewAsync(_outputWriter)) { var validationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, ValidateIssuerSigningKey = true, ValidateLifetime = true }; var reader = new JwtTokenReader(validationParameters, testOpenIdServer.OpenIdAddressConfiguration); testServer.AddFilter(filters => filters.AddJwtTokenAuthorization(options => options.JwtTokenReader = reader)); using (HttpClient client = testServer.CreateClient()) using (var request = new HttpRequestMessage(HttpMethod.Get, HealthController.Route)) { string accessToken = testOpenIdServer.RequestSecretToken(issuer, authority, privateKey, 7); request.Headers.Add(JwtTokenAuthorizationOptions.DefaultHeaderName, accessToken); // Act using (HttpResponseMessage response = await client.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task SendRequest_WithCorrelateOptionsNotAllowTransactionInRequest_ResponseWithBadRequest() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation(opt => opt.Transaction.AllowInRequest = false)) .Configure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CorrelationController.GetRoute) .WithHeader(DefaultTransactionId, $"transaction-{Guid.NewGuid()}"); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); Assert.DoesNotContain(response.Headers, header => header.Key == DefaultOperationId); Assert.DoesNotContain(response.Headers, header => header.Key == DefaultTransactionId); } } }
public async Task GetHealthWithIncorrectBearerToken_WithAzureManagedIdentityAuthorization_ReturnsUnauthorized() { // Arrange string issuer = _bogusGenerator.Internet.Url(); string authority = _bogusGenerator.Internet.Url(); string privateKey = GenerateRandomPrivateKey(); await using (var testOpenIdServer = await TestOpenIdServer.StartNewAsync(_logger)) { TokenValidationParameters validationParameters = testOpenIdServer.GenerateTokenValidationParametersWithValidAudience(issuer, authority, privateKey); var reader = new JwtTokenReader(validationParameters, testOpenIdServer.OpenIdAddressConfiguration); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => { opt.Filters.AddJwtTokenAuthorization(jwt => jwt.JwtTokenReader = reader); }); }); await using (var testApiServer = await TestApiServer.StartNewAsync(options, _logger)) { var accessToken = $"Bearer {_bogusGenerator.Random.AlphaNumeric(10)}.{_bogusGenerator.Random.AlphaNumeric(50)}.{_bogusGenerator.Random.AlphaNumeric(40)}"; var request = HttpRequestBuilder .Get(HealthController.GetRoute) .WithHeader(JwtTokenAuthorizationOptions.DefaultHeaderName, accessToken); // Act using (HttpResponseMessage response = await testApiServer.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task GetHealthWithCorrectBearerToken_WithNullReaderAzureManagedIdentityAuthorization_ReturnsOk() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => { opt.Filters.AddJwtTokenAuthorization(jwt => jwt.AddJwtTokenReader(serviceProvider => null)); }); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(HealthController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); } } }
public async Task GetHealthWithoutBearerToken_WithIncorrectAzureManagedIdentityAuthorization_ReturnsUnauthorized() { // Arrange await using (var testOpenIdServer = await TestOpenIdServer.StartNewAsync(_logger)) { var validationParameters = new TokenValidationParameters { ValidateAudience = false, ValidateIssuer = false, ValidateIssuerSigningKey = true, ValidateLifetime = true }; var reader = new JwtTokenReader(validationParameters, testOpenIdServer.OpenIdAddressConfiguration); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => { opt.Filters.AddJwtTokenAuthorization(jwt => jwt.JwtTokenReader = reader); }); }); await using (var testApiServer = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(HealthController.GetRoute); // Act using (HttpResponseMessage response = await testApiServer.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task IncomingJson_WithOnlyAllowJsonFormatting_Succeeds() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(opt => { opt.InputFormatters.Add(new PlainTextInputFormatter()); opt.OnlyAllowJsonFormatting(); })); var country = new Country { Name = BogusGenerator.Address.Country(), Code = BogusGenerator.Random.Enum <CountryCode>() }; string json = JsonSerializer.Serialize(country); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CountryController.GetJsonRoute) .WithJsonBody(json); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/json", response.Content.Headers.ContentType.MediaType); string content = await response.Content.ReadAsStringAsync(); Assert.Equal(json, content); } } }
public async Task SendRequest_WithCorrelateOptionsCustomGenerateTransactionId_ResponseWitCustomGeneratedTransactionId() { // Arrange var expectedTransactionId = $"transaction-{Guid.NewGuid():N}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation(opt => opt.Transaction.GenerateId = () => expectedTransactionId)) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CorrelationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains(response.Headers, header => header.Key == DefaultOperationId); string actualTransactionId = GetResponseHeader(response, DefaultTransactionId); Assert.Equal(expectedTransactionId, actualTransactionId); } } }
public async Task SendRequestWithAlternateCorrelationAccessor_RemovesCorrelation_ResponseWithoutCorrelationInfo() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation() .AddScoped <IHttpCorrelationInfoAccessor, NullCorrelationInfoAccessor>()) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(EchoController.GetPostRoute) .WithHeader(DefaultOperationId, $"operation-{Guid.NewGuid()}") .WithHeader(DefaultTransactionId, $"transaction-{Guid.NewGuid()}"); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.DoesNotContain(response.Headers, header => header.Key == DefaultOperationId); Assert.DoesNotContain(response.Headers, header => header.Key == DefaultTransactionId); } } }
/// <summary> /// Initializes a new instance of the <see cref="CertificateAuthenticationFilterTests"/> class. /// </summary> public CertificateAuthenticationFilterTests(ITestOutputHelper outputWriter) { _testServer = new TestApiServer(outputWriter); }
/// <summary> /// Initializes a new instance of the <see cref="SharedAccessKeyAuthenticationAttributeTests"/> class. /// </summary> public SharedAccessKeyAuthenticationAttributeTests(ITestOutputHelper outputWriter) { _testServer = new TestApiServer(outputWriter); }
/// <summary> /// Initializes a new instance of the <see cref="JwtTokenAuthorizationFilterTests"/> class. /// </summary> public JwtTokenAuthorizationFilterTests(ITestOutputHelper outputWriter) { _outputWriter = outputWriter; _testServer = new TestApiServer(_outputWriter); }
public HttpMetricsMiddlewareShould(ITestOutputHelper testOutputHelper) { _udpListener = new UdpListener(testOutputHelper, 2); _testApiServer = new TestApiServer(_udpListener.Port); }