public async Task SharedAccessKeyAuthorizedRoute_DoesntEmitSecurityEventsByDefault_RunsAuthentication() { // Arrange var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => services.AddSecretStore(stores => stores.AddInMemory(SecretName, $"secret-{Guid.NewGuid()}"))) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(SharedAccessKeyAuthenticationController.AuthorizedGetRoute); // 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 SendRequest_WithSerilogCorrelationEnrichment_ReturnsOkWithEnrichedCorrelationLogProperties() { // 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 request = HttpRequestBuilder.Get(CorrelationController.GetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); CorrelationInfo correlationInfo = await AssertAppCorrelationInfoAsync(response); AssertLoggedCorrelationProperties(spySink, correlationInfo); } } }
public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailOnInvalidBase64Format() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .Build()); services.AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(NoneAuthenticationController.GetRoute) .WithHeader("X-ARR-ClientCert", "something not even close to an client certificate export"); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } }
public async Task GetHealthWithCorrectBearerToken_WithAzureManagedIdentityAuthorization_ReturnsOk() { // Arrange string issuer = _bogusGenerator.Internet.Url(); string authority = _bogusGenerator.Internet.Url(); string privateKey = GenerateRandomPrivateKey(); await using (var testOpenIdServer = await TestOpenIdServer.StartNewAsync(_logger)) { var options = new TestApiServerOptions() .ConfigureServices(services => { TokenValidationParameters tokenValidationParameters = testOpenIdServer.GenerateTokenValidationParametersWithValidAudience(issuer, authority, privateKey); var reader = new JwtTokenReader(tokenValidationParameters, testOpenIdServer.OpenIdAddressConfiguration); 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.OK, response.StatusCode); } } } }
public async Task IncomingText_WithoutOnlyAllowJsonFormatting_Succeeds() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(opt => { opt.InputFormatters.Add(new PlainTextInputFormatter()); })); string sentence = BogusGenerator.Lorem.Sentence(); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CountryController.GetPlainTextRoute) .WithTextBody(sentence); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("text/plain", response.Content.Headers.ContentType.MediaType); string actual = await response.Content.ReadAsStringAsync(); Assert.Equal(sentence, actual); } } }
public async Task IncomingModel_WithConfigureJsonFormattingOnEnums_SerializeAsStrings() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(mvcOptions => { mvcOptions.ConfigureJsonFormatting(jsonOptions => jsonOptions.Converters.Add(new JsonStringEnumConverter())); })); 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); string actual = await response.Content.ReadAsStringAsync(); Assert.Contains(country.Code.ToString(), actual); } } }
public async Task SharedAccessKeyAuthorizedRoute_EmitsSecurityEventsWhenRequested_RunsAuthentication(bool emitsSecurityEvents) { // Arrange var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddSecretStore(stores => stores.AddInMemory(SecretName, $"secret-{Guid.NewGuid()}")) .AddMvc(opt => opt.Filters.AddSharedAccessKeyAuthenticationOnHeader(HeaderName, SecretName, authOptions => { authOptions.EmitSecurityEvents = emitsSecurityEvents; })); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); 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.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.True(emitsSecurityEvents == logEvents.Any(logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); })); } } }
public async Task IncomingText_WithOnlyAllowJsonFormatting_Fails() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(opt => { opt.InputFormatters.Add(new PlainTextInputFormatter()); opt.OnlyAllowJsonFormatting(); })); string sentence = BogusGenerator.Lorem.Sentence(); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CountryController.GetPlainTextRoute) .WithTextBody(sentence); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.UnsupportedMediaType, response.StatusCode); } } }
public async Task SendRequest_WithCorrelateOptionsUpstreamServiceCustomOperationParentIHeaderName_UsesUpstreamOperationId(bool extractFromRequest) { // Arrange var operationId = Guid.NewGuid().ToString(); var operationParentId = $"|{operationId}.{Guid.NewGuid()}"; var operationParentIdHeaderName = "My-Request-Id"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation(opt => { opt.UpstreamService.ExtractFromRequest = extractFromRequest; opt.UpstreamService.OperationParentIdHeaderName = operationParentIdHeaderName; })) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CorrelationController.GetRoute) .WithHeader(operationParentIdHeaderName, operationParentId); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); string actualOperationId = GetResponseHeader(response, DefaultOperationId); Assert.Equal(extractFromRequest, operationId == actualOperationId); } } }
public async Task SendRequestWithCorrelateInfo_SetsCorrelationInfo_ResponseWithUpdatedCorrelationInfo() { // Arrange var correlationInfo = new CorrelationInfo($"operation-{Guid.NewGuid()}", $"transaction-{Guid.NewGuid()}"); var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation()) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Post(CorrelationController.SetCorrelationRoute) .WithHeader(DefaultOperationId, correlationInfo.OperationId) .WithHeader(DefaultTransactionId, correlationInfo.TransactionId); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); string actualOperationId = GetResponseHeader(response, DefaultOperationId); string actualTransactionId = GetResponseHeader(response, DefaultTransactionId); Assert.Equal(correlationInfo.OperationId, actualOperationId); Assert.Equal(correlationInfo.TransactionId, actualTransactionId); } } }
public async Task SendRequest_WithRequestIdHeader_ResponseWithDifferentRequestIdHeader() { // Arrange string expected = $"operation-{Guid.NewGuid()}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation()) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder .Get(CorrelationController.GetRoute) .WithHeader(DefaultOperationId, expected); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); string actual = GetResponseHeader(response, DefaultOperationId); Assert.NotEqual(expected, actual); } } }
public async Task SendRequest_WithCorrelateOptionsCustomGenerateOperationId_ResponseWitCustomGeneratedOperationId() { // Arrange var expectedOperationId = $"operation-{Guid.NewGuid():N}"; var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation(opt => opt.Operation.GenerateId = () => expectedOperationId)) .PreConfigure(app => app.UseTraceIdentifier(opt => opt.EnableTraceIdentifier = false) .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 == DefaultTransactionId); string actualOperationId = GetResponseHeader(response, DefaultOperationId); Assert.Equal(expectedOperationId, actualOperationId); } } }
public async Task SendRequest_WithoutCorrelationHeaders_ResponseWithCorrelationHeadersAndCorrelationAccess() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation()) .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); string correlationId = GetResponseHeader(response, DefaultTransactionId); string requestId = GetResponseHeader(response, DefaultOperationId); string json = await response.Content.ReadAsStringAsync(); var content = JsonConvert.DeserializeAnonymousType(json, new { TransactionId = "", OperationId = "" }); Assert.False(String.IsNullOrWhiteSpace(content.TransactionId), "Accessed 'X-Transaction-ID' cannot be blank"); Assert.False(String.IsNullOrWhiteSpace(content.OperationId), "Accessed 'X-Operation-ID' cannot be blank"); Assert.Equal(correlationId, content.TransactionId); Assert.Equal(requestId, content.OperationId); } } }
public async Task GetHealthWithCorrectBearerToken_WithInjectedAzureManagedIdentityAuthorization_ReturnsOk() { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => { opt.Filters.AddJwtTokenAuthorization(jwt => jwt.AddJwtTokenReader <IgnoredJwtTokenReader>()); }); }); await using (var server = 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 server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } }
public async Task JwtAuthorizedRoute_EmitSecurityEventsWhenRequested_RunsAuthorization(bool emitSecurityEvents) { // Arrange var spySink = new InMemorySink(); var options = new TestApiServerOptions() .ConfigureServices(services => { services.AddMvc(opt => opt.Filters.AddJwtTokenAuthorization(jwt => jwt.EmitSecurityEvents = emitSecurityEvents)); }) .ConfigureHost(host => host.UseSerilog((context, config) => config.WriteTo.Sink(spySink))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { string accessToken = $"Bearer {_bogusGenerator.Random.AlphaNumeric(10)}.{_bogusGenerator.Random.AlphaNumeric(50)}"; var request = HttpRequestBuilder .Get(HealthController.GetRoute) .WithHeader(JwtTokenAuthorizationOptions.DefaultHeaderName, accessToken); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); IEnumerable <LogEvent> logEvents = spySink.DequeueLogEvents(); Assert.True(emitSecurityEvents == logEvents.Any(logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); })); } } }
public async Task AuthorizedRoute_WithCertificateAuthentication_ShouldFailWithUnauthorized_WhenClientCertificateSubjectNameDoesntMatch() { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithSubject("unrecognized-subject-name")) { 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); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Arrange Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task CertificateAuthorizedRoute_WithBypassAttribute_SkipsAuthentication(string route) { // Arrange const string issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName("issuer", "subject")) { 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")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(route); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } } }
public async Task SetupApi_WithoutApplicationVersion_Throws() { // Arrange var options = new TestApiServerOptions() .Configure(app => app.UseVersionTracking()); // Act / Assert await Assert.ThrowsAsync <InvalidOperationException>(() => TestApiServer.StartNewAsync(options, _logger)); }
public async Task AuthenticationOperationFilter_ShouldNotIncludeSecurityDefinitionResponses_OnNonAuthorizedOperations() { // Arrange string assemblyName = typeof(AuthenticationOperationFilterTests).Assembly.GetName().Name; var options = new TestApiServerOptions() .ConfigureServices(services => { var openApiInformation = new OpenApiInfo { Title = assemblyName, Version = "v1" }; services.AddSwaggerGen(swagger => { swagger.SwaggerDoc("v1", openApiInformation); swagger.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, assemblyName + ".Open-Api.xml")); swagger.OperationFilter <OAuthAuthorizeOperationFilter>(new object[] { new[] { "myApiScope" } }); swagger.OperationFilter <SharedAccessKeyAuthenticationOperationFilter>("sharedaccesskey", SecuritySchemeType.ApiKey); swagger.OperationFilter <CertificateAuthenticationOperationFilter>("certificate", SecuritySchemeType.ApiKey); }); }) .Configure(app => { app.UseSwagger(); app.UseSwaggerUI(swagger => { swagger.SwaggerEndpoint("v1/swagger.json", assemblyName); swagger.DocumentTitle = assemblyName; }); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get("/swagger/v1/swagger.json"); using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); OpenApiDocument swagger = await ReadSwaggerOpenAPiDocumentAsync(response); Assert.True(swagger.Paths.TryGetValue("/" + AuthenticationController.NoneRoute, out OpenApiPathItem path), $"Cannot find /{AuthenticationController.NoneRoute} none authorized path in Open API spec file"); Assert.True(path.Operations.TryGetValue(OperationType.Get, out OpenApiOperation operation), "Cannot find GET operation in Open API spec file"); OpenApiResponses operationResponses = operation.Responses; Assert.DoesNotContain(operationResponses, r => r.Key == "401"); Assert.DoesNotContain(operationResponses, r => r.Key == "403"); } } }
public async Task SendRequest_WithVersionTrackingForBlankVersion_DoesntAddApplicationVersionToResponse(string version) { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddAppVersion(provider => new StubAppVersion(version))) .Configure(app => app.UseVersionTracking()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(HealthController.GetRoute); using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.False(response.Headers.Contains(DefaultHeaderName)); } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationViaConfigurationAndSecretProvider_ShouldFailWithUnauthorized_WhenAnyClientCertificateValidationDoesntSucceeds( string subjectValue, string issuerValue, bool expected) { // Arrange const string subjectKey = "subject", issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName(issuerValue, subjectValue)) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(subjectKey, "CN=known-subject") })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, subjectKey) .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=known-issuername")) .AddClientCertificate(clientCertificate) .AddSingleton(certificateValidator) .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 AuthorizedRoute_WithCertificateAuthenticationViaConfiguration_ShouldFailWithUnauthorized_WhenAnyClientCertificateValidationDoesntSucceeds( string subjectName, string issuerName, string thumbprintNoise, bool expected) { // Arrange using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName(issuerName, subjectName)) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(SubjectKey, "CN=subject"), new KeyValuePair <string, string>(IssuerKey, "CN=issuer"), new KeyValuePair <string, string>(ThumbprintKey, clientCertificate.Thumbprint + thumbprintNoise) })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, SubjectKey) .WithIssuer(X509ValidationLocation.Configuration, IssuerKey) .WithThumbprint(X509ValidationLocation.Configuration, ThumbprintKey) .Build()); services.AddSingleton(certificateValidator) .AddClientCertificate(clientCertificate); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CertificateAuthenticationOnMethodController.AuthorizedGetRoute); // 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 JwtAuthorizedRoute_WithBypassAttribute_SkipsAuthorization(string route) { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(opt => opt.Filters.AddJwtTokenAuthorization())); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(route); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } }
public async Task SendRequest_WithCorrelationInfoOptionsNotGenerateTransactionId_ResponseWithoutTransactionId() { var options = new TestApiServerOptions() .ConfigureServices(services => services.AddHttpCorrelation(opt => opt.Transaction.GenerateWhenNotSpecified = false)) .PreConfigure(app => app.UseHttpCorrelation()); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(CorrelationController.GetRoute); using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Contains(response.Headers, header => header.Key == DefaultOperationId); Assert.DoesNotContain(response.Headers, header => header.Key == DefaultTransactionId); } } }
public async Task SharedAccessKeyAuthorizedRoute_WithBypassAttributeOnMethod_SkipsAuthentication(string route) { // Arrange var options = new TestApiServerOptions() .ConfigureServices(services => services.AddMvc(opt => opt.Filters.AddSharedAccessKeyAuthenticationOnHeader(HeaderName, SecretName))); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { var request = HttpRequestBuilder.Get(route); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.OK, response.StatusCode); } } }
public async Task AuthorizedRoute_WithCertificateAuthenticationInHeader_ShouldSucceed() { // Arrange const string subjectKey = "subject", issuerKey = "issuer"; using (X509Certificate2 clientCertificate = SelfSignedCertificate.CreateWithIssuerAndSubjectName("known-issuername", "known-subject")) { var options = new TestApiServerOptions() .ConfigureAppConfiguration(config => config.AddInMemoryCollection(new [] { new KeyValuePair <string, string>(subjectKey, "CN=known-subject") })) .ConfigureServices(services => { var certificateValidator = new CertificateAuthenticationValidator( new CertificateAuthenticationConfigBuilder() .WithSubject(X509ValidationLocation.Configuration, subjectKey) .WithIssuer(X509ValidationLocation.SecretProvider, issuerKey) .Build()); services.AddSecretStore(stores => stores.AddInMemory(issuerKey, "CN=known-issuername")) .AddSingleton(certificateValidator) .AddMvc(opt => opt.Filters.AddCertificateAuthentication()); }); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { string base64String = Convert.ToBase64String(clientCertificate.Export(X509ContentType.Pkcs12), Base64FormattingOptions.None); var request = HttpRequestBuilder .Get(NoneAuthenticationController.GetRoute) .WithHeader("X-ARR-ClientCert", base64String); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.NotEqual(HttpStatusCode.Unauthorized, response.StatusCode); } } } }
public async Task AuthorizedRoute_WithSharedAccessKey_ShouldFailWithKeyNotFoundException_WhenNoSecretProviderWasRegistered() { // Arrange string secretValue = $"secret-{Guid.NewGuid()}"; var options = new TestApiServerOptions(); await using (var server = await TestApiServer.StartNewAsync(options, _logger)) { // Act var request = HttpRequestBuilder .Get(SharedAccessKeyAuthenticationController.AuthorizedGetRoute) .WithHeader(HeaderName, secretValue); using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); } } }
public async Task SharedAccessKeyAuthorizedRoute_EmitsSecurityEventsWhenRequested_RunsAuthentication(bool emitsSecurityEvents) { // 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(authOptions => { authOptions.EmitSecurityEvents = emitsSecurityEvents; })); }) .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.True(emitsSecurityEvents == logEvents.Any(logEvent => { string message = logEvent.RenderMessage(); return(message.Contains("EventType") && message.Contains("Security")); })); } } }
public async Task AuthorizedRoute_WithSharedAccessKey_ShouldFailWithUnauthorized(string headerName, string headerValue) { // 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, headerValue); // Act using (HttpResponseMessage response = await server.SendAsync(request)) { // Assert Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } } }
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); } } }