|
1 | 1 | // Copyright (c) Microsoft Corporation. |
2 | 2 | // Licensed under the MIT License. |
3 | 3 |
|
| 4 | +using System; |
4 | 5 | using System.Collections.Generic; |
| 6 | +using System.IO; |
| 7 | +using System.Net; |
| 8 | +using System.Net.Http; |
5 | 9 | using System.Threading.Tasks; |
| 10 | +using Azure.DataApiBuilder.Config.ObjectModel; |
| 11 | +using Azure.DataApiBuilder.Core.Authorization; |
| 12 | +using Azure.DataApiBuilder.Core.Configurations; |
| 13 | +using Microsoft.AspNetCore.TestHost; |
6 | 14 | using Microsoft.VisualStudio.TestTools.UnitTesting; |
7 | 15 |
|
8 | 16 | namespace Azure.DataApiBuilder.Service.Tests.SqlTests.RestApiTests.Find |
@@ -661,5 +669,107 @@ await SetupAndRunRestApiTest( |
661 | 669 | ); |
662 | 670 | } |
663 | 671 | #endregion |
| 672 | + |
| 673 | + #region RestApiTests Outliers |
| 674 | + |
| 675 | + /// <summary> |
| 676 | + /// Tests we ensure that an invalid query in the EasyAuth header |
| 677 | + /// retruns a successful request without executing the invalid query. |
| 678 | + /// </summary> |
| 679 | + [TestCategory(TestCategory.MSSQL)] |
| 680 | + [TestMethod] |
| 681 | + public async Task TestInvalidQueryInHeader() |
| 682 | + { |
| 683 | + TestHelper.SetupDatabaseEnvironment(TestCategory.MSSQL); |
| 684 | + |
| 685 | + string firstHeader = @" |
| 686 | + { |
| 687 | + ""auth_typ"":""aad"", |
| 688 | + ""claims"":[ |
| 689 | + { |
| 690 | + ""typ"":""x', N'v';SET IDENTITY_INSERT authors ON;--"", |
| 691 | + ""val"":""x"" |
| 692 | + } |
| 693 | + ], |
| 694 | + ""UserRoles"":[""authenticated""] |
| 695 | + }"; |
| 696 | + |
| 697 | + string firstGeneratedToken = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(firstHeader)); |
| 698 | + |
| 699 | + string secondHeader = @" |
| 700 | + { |
| 701 | + ""auth_typ"":""aad"", |
| 702 | + ""claims"":[ |
| 703 | + { |
| 704 | + ""typ"":""x', N'v';INSERT INTO authors (id, name, birthdate) VALUES (10001, 'Hidden Author', '2001-01-01');--"", |
| 705 | + ""val"":""x"" |
| 706 | + } |
| 707 | + ], |
| 708 | + ""UserRoles"":[""authenticated""] |
| 709 | + }"; |
| 710 | + |
| 711 | + string secondGeneratedToken = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(secondHeader)); |
| 712 | + |
| 713 | + const string SESSION_CONFIG = $"session-context-config.{TestCategory.MSSQL}.json"; |
| 714 | + RuntimeConfigProvider configProvider = |
| 715 | + TestHelper.GetRuntimeConfigProvider(TestHelper.GetRuntimeConfigLoader()); |
| 716 | + RuntimeConfig config = configProvider.GetConfig(); |
| 717 | + |
| 718 | + RuntimeConfig updatedConfig = config with |
| 719 | + { |
| 720 | + DataSource = config.DataSource! with |
| 721 | + { |
| 722 | + Options = new Dictionary<string, object?> |
| 723 | + { |
| 724 | + // Matches MsSqlOptions.SetSessionContext (hyphenated naming policy). |
| 725 | + { "set-session-context", true } |
| 726 | + } |
| 727 | + }, |
| 728 | + Runtime = config.Runtime! with |
| 729 | + { |
| 730 | + Host = config.Runtime.Host! with |
| 731 | + { |
| 732 | + Authentication = new AuthenticationOptions( |
| 733 | + Provider: EasyAuthType.AppService.ToString(), Jwt: null) |
| 734 | + } |
| 735 | + } |
| 736 | + }; |
| 737 | + |
| 738 | + File.WriteAllText(SESSION_CONFIG, updatedConfig.ToJson()); |
| 739 | + |
| 740 | + string[] args = [$"--ConfigFileName={SESSION_CONFIG}"]; |
| 741 | + using TestServer server = new(Program.CreateWebHostBuilder(args)); |
| 742 | + using HttpClient client = server.CreateClient(); |
| 743 | + |
| 744 | + // Request with the first header |
| 745 | + HttpRequestMessage requestWithHeader = new(HttpMethod.Get, "api/Author"); |
| 746 | + requestWithHeader.Headers.Add(AuthenticationOptions.CLIENT_PRINCIPAL_HEADER, firstGeneratedToken); |
| 747 | + requestWithHeader.Headers.Add(AuthorizationResolver.CLIENT_ROLE_HEADER, "authenticated"); |
| 748 | + HttpResponseMessage responseWithHeader = await client.SendAsync(requestWithHeader); |
| 749 | + Assert.AreEqual(expected: HttpStatusCode.OK, actual: responseWithHeader.StatusCode); |
| 750 | + |
| 751 | + // Request with second header |
| 752 | + HttpRequestMessage requestWithHeaderSec = new(HttpMethod.Get, "api/Author"); |
| 753 | + requestWithHeaderSec.Headers.Add(AuthenticationOptions.CLIENT_PRINCIPAL_HEADER, secondGeneratedToken); |
| 754 | + requestWithHeaderSec.Headers.Add(AuthorizationResolver.CLIENT_ROLE_HEADER, "authenticated"); |
| 755 | + HttpResponseMessage responseWithHeaderSec = await client.SendAsync(requestWithHeaderSec); |
| 756 | + Assert.AreEqual(expected: HttpStatusCode.OK, actual: responseWithHeaderSec.StatusCode); |
| 757 | + |
| 758 | + HttpRequestMessage request = new(HttpMethod.Get, $"api/Author/id/10001"); |
| 759 | + HttpResponseMessage response = await client.SendAsync(request); |
| 760 | + string responseBody = await response.Content.ReadAsStringAsync(); |
| 761 | + |
| 762 | + Assert.IsFalse(responseBody.Contains($"\"id\":10001"), |
| 763 | + "The GET request should not return the invalid row."); |
| 764 | + Assert.IsFalse(responseBody.Contains("Hidden Author"), |
| 765 | + "The GET request should not return any information related to the invalid row."); |
| 766 | + |
| 767 | + if (File.Exists(SESSION_CONFIG)) |
| 768 | + { |
| 769 | + File.Delete(SESSION_CONFIG); |
| 770 | + } |
| 771 | + } |
| 772 | + |
| 773 | + #endregion |
664 | 774 | } |
665 | 775 | } |
0 commit comments