Skip to content

Commit 63ad2a5

Browse files
Basic Authentication user ID was not properly parsed when the password contained the special separator character ':'.
1 parent 4e10eec commit 63ad2a5

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

dotnet/src/dotnetcore/GxClasses/Helpers/BasicAuthentication.cs

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System;
32
using System.Text;
43

54
namespace GxClasses.Helpers
65
{
76
public class BasicAuthenticationHeaderValue
87
{
8+
const char UserNamePasswordSeparator= ':';
99
public BasicAuthenticationHeaderValue(string authenticationHeaderValue)
1010
{
1111
if (!string.IsNullOrWhiteSpace(authenticationHeaderValue))
@@ -19,7 +19,7 @@ public BasicAuthenticationHeaderValue(string authenticationHeaderValue)
1919
}
2020

2121
private readonly string _authenticationHeaderValue;
22-
private string[] _splitDecodedCredentials;
22+
private string _usernamePassword;
2323

2424
public bool IsValidBasicAuthenticationHeaderValue { get; private set; }
2525
public string UserIdentifier { get; private set; }
@@ -32,11 +32,11 @@ private bool TryDecodeHeaderValue()
3232
{
3333
return false;
3434
}
35-
var encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
35+
string encodedCredentials = _authenticationHeaderValue.Substring(headerSchemeLength);
3636
try
3737
{
38-
var decodedCredentials = Convert.FromBase64String(encodedCredentials);
39-
_splitDecodedCredentials = System.Text.Encoding.ASCII.GetString(decodedCredentials).Split(':');
38+
byte[] decodedCredentials = Convert.FromBase64String(encodedCredentials);
39+
_usernamePassword = Encoding.ASCII.GetString(decodedCredentials);
4040
return true;
4141
}
4242
catch (FormatException)
@@ -47,13 +47,19 @@ private bool TryDecodeHeaderValue()
4747

4848
private void ReadAuthenticationHeaderValue()
4949
{
50-
IsValidBasicAuthenticationHeaderValue = _splitDecodedCredentials!= null && _splitDecodedCredentials.Length == 2
51-
&& !string.IsNullOrWhiteSpace(_splitDecodedCredentials[0])
52-
&& !string.IsNullOrWhiteSpace(_splitDecodedCredentials[1]);
50+
IsValidBasicAuthenticationHeaderValue = !string.IsNullOrEmpty(_usernamePassword) && _usernamePassword.Contains(UserNamePasswordSeparator);
5351
if (IsValidBasicAuthenticationHeaderValue)
5452
{
55-
UserIdentifier = _splitDecodedCredentials[0];
56-
UserPassword = _splitDecodedCredentials[1];
53+
int separatorIndex = _usernamePassword.IndexOf(UserNamePasswordSeparator);
54+
UserIdentifier = _usernamePassword.Substring(0, separatorIndex);
55+
if (separatorIndex + 1 < _usernamePassword.Length)
56+
{
57+
UserPassword = _usernamePassword.Substring(separatorIndex + 1);
58+
}
59+
else
60+
{
61+
UserPassword = string.Empty;
62+
}
5763
}
5864
}
5965
}

dotnet/test/DotNetCoreWebUnitTest/Middleware/HeadersTest.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Reflection;
44
using System.Threading.Tasks;
55
using GeneXus.Metadata;
6+
using GeneXus.Utils;
67
using Xunit;
78
namespace xUnitTesting
89
{
@@ -25,14 +26,18 @@ public async Task TestForwardedHeaders()
2526
const string host = "192.168.1.100";
2627
const string scheme = "https";
2728
const string remoteUrl = $"{scheme}:\\/\\/{host}";
29+
const string passwordWithSpecialCharacters = "mypasswordwithspecialcharacters:!*";
30+
const string userId = "myuser";
2831
HttpClient client = server.CreateClient();
2932
client.DefaultRequestHeaders.Add("X-Forwarded-For", host);
3033
client.DefaultRequestHeaders.Add("X-Forwarded-Proto", scheme);
34+
client.DefaultRequestHeaders.Add("Authorization", $"Basic {StringUtil.ToBase64(userId+ ":" + passwordWithSpecialCharacters)}");
3135

3236
HttpResponseMessage response = await client.GetAsync("/rest/apps/httpheaders");
3337
response.EnsureSuccessStatusCode();
3438
string resp = await response.Content.ReadAsStringAsync();
35-
Assert.Contains(remoteUrl, resp, System.StringComparison.OrdinalIgnoreCase);
39+
Assert.Contains(remoteUrl, resp, StringComparison.OrdinalIgnoreCase);
40+
Assert.Contains(userId, resp, StringComparison.OrdinalIgnoreCase);
3641
Assert.Equal(System.Net.HttpStatusCode.OK, response.StatusCode);
3742
}
3843

dotnet/test/DotNetCoreWebUnitTest/apps/httpheaders.cs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
using System;
12
using GeneXus.Application;
23
using GeneXus.Data.NTier;
4+
using GeneXus.Data.NTier.ADO;
35
using GeneXus.Procedure;
6+
using GeneXus.Utils;
47

58
namespace GeneXus.Programs.apps
69
{
@@ -30,7 +33,7 @@ public void execute(out string result)
3033
void executePrivate(out string result)
3134
{
3235
result = (context.GetHttpSecure() == 1 ? "https://" : "http://") + context.GetRemoteAddress();
33-
36+
result += StringUtil.NewLine() + GXUtil.UserId(string.Empty, context, pr_default);
3437
cleanup();
3538
}
3639

@@ -51,7 +54,37 @@ protected void CloseOpenCursors()
5154

5255
public override void initialize()
5356
{
57+
pr_default = new DataStoreProvider(context, new httpheaders__default(),
58+
new Object[][] {
59+
}
60+
);
5461
}
62+
private IDataStoreProvider pr_default;
5563
}
64+
public class httpheaders__default : DataStoreHelperBase, IDataStoreHelper
65+
{
66+
public ICursor[] getCursors()
67+
{
68+
cursorDefinitions();
69+
return new Cursor[] {
70+
};
71+
}
72+
73+
private static CursorDef[] def;
74+
private void cursorDefinitions()
75+
{
76+
if (def == null)
77+
{
78+
def = new CursorDef[] {
79+
};
80+
}
81+
}
5682

83+
public void getResults(int cursor,
84+
IFieldGetter rslt,
85+
Object[] buf)
86+
{
87+
}
88+
89+
}
5790
}

0 commit comments

Comments
 (0)