Skip to content

Commit ad8be9b

Browse files
committed
2.24.0 Initial
1 parent 454a252 commit ad8be9b

17 files changed

Lines changed: 303 additions & 142 deletions

NpgsqlRest/Auth/AuthHandler.cs

Lines changed: 169 additions & 104 deletions
Large diffs are not rendered by default.

NpgsqlRest/Auth/NpgsqlRestAuthenticationOptions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,18 @@ public class NpgsqlRestAuthenticationOptions
8989
/// This is used to verify the password from the password parameter when login endpoint returns a hash of the password (see HashColumnName).
9090
/// </summary>
9191
public string PasswordParameterNameContains { get; set; } = "pass";
92+
93+
/// <summary>
94+
/// Default password hasher object. Inject custom password hasher object to add default password hasher.
95+
/// </summary>
96+
public IPasswordHasher? PasswordHasher { get; set; } = new PasswordHasher();
97+
98+
/// <summary>
99+
/// Command that is executed when the password verification fails. There are three text parameters:
100+
/// - scheme: authentication scheme used for the login (if exists)
101+
/// - user_name: user id used for the login (if exists)
102+
/// - user_id: user id used for the login (if exists)
103+
/// Please use PostgreSQL parameter placeholders for the parameters ($1, $2, $3).
104+
/// </summary>
105+
public string? PasswordVerificationFailedCommand { get; set; } = null;
92106
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace NpgsqlRest.Auth;
4+
5+
public partial class PostgreSqlParameterCounter
6+
{
7+
[GeneratedRegex(@"\$\d+")]
8+
public static partial Regex PostgreSqlParameterPattern();
9+
10+
public static int CountParameters(string sql)
11+
{
12+
return PostgreSqlParameterPattern().Matches(sql).Count;
13+
}
14+
}

NpgsqlRest/Log.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,7 @@ public static partial class Log
178178

179179
[LoggerMessage(Level = LogLevel.Information, Message = "{description} has set to use multiple UPLOAD HANDLERES {handlers} by the comment annotation.")]
180180
public static partial void CommentUploadHandlers(this ILogger logger, string description, string[]? handlers);
181+
182+
[LoggerMessage(Level = LogLevel.Warning, Message = "Login endpoint {endpoint} failed to locate the password parameter in parameter collection {parameters}. Password parameter is the first that contains \"{contains}\" text in parameter name.")]
183+
public static partial void CantFindPasswordParameter(this ILogger logger, string endpoint, string?[]? parameters, string contains);
181184
}

NpgsqlRest/NpgsqlRest.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@
2929
<GenerateDocumentationFile>true</GenerateDocumentationFile>
3030
<PackageReadmeFile>README.MD</PackageReadmeFile>
3131
<DocumentationFile>bin\$(Configuration)\$(AssemblyName).xml</DocumentationFile>
32-
<Version>2.23.0</Version>
33-
<AssemblyVersion>2.23.0</AssemblyVersion>
34-
<FileVersion>2.23.0</FileVersion>
35-
<PackageVersion>2.23.0</PackageVersion>
32+
<Version>2.24.0</Version>
33+
<AssemblyVersion>2.24.0</AssemblyVersion>
34+
<FileVersion>2.24.0</FileVersion>
35+
<PackageVersion>2.24.0</PackageVersion>
3636
</PropertyGroup>
3737

3838
<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">

NpgsqlRest/NpgsqlRestMiddleware.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ public async Task InvokeAsync(HttpContext context)
311311
}
312312
else
313313
{
314-
parameter.Value = options.PasswordHasher?.HashPassword(hashValueQueryDict) as object ?? DBNull.Value;
314+
parameter.Value = options.AuthenticationOptions.PasswordHasher?.HashPassword(hashValueQueryDict) as object ?? DBNull.Value;
315315
}
316316
}
317317
if (parameter.UploadMetadata is true)
@@ -749,7 +749,7 @@ await options.ValidateParametersAsync(new ParameterValidationValues(
749749
}
750750
else
751751
{
752-
parameter.Value = options.PasswordHasher?.HashPassword(hashValueBodyDict) as object ?? DBNull.Value;
752+
parameter.Value = options.AuthenticationOptions.PasswordHasher?.HashPassword(hashValueBodyDict) as object ?? DBNull.Value;
753753
}
754754
}
755755
if (parameter.UploadMetadata is true)

NpgsqlRest/NpgsqlRestOptions.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,6 @@ public NpgsqlRestOptions(NpgsqlDataSource dataSource)
316316
/// </summary>
317317
public IResponseParser? DefaultResponseParser { get; set; }
318318

319-
/// <summary>
320-
/// Default password hasher object. Inject custom password hasher object to add default password hasher.
321-
/// </summary>
322-
public IPasswordHasher PasswordHasher { get; set; } = new PasswordHasher();
323-
324319
/// <summary>
325320
/// Default upload handler options.
326321
/// Set this option to null to disable upload handlers or use this to modify upload handler options.

NpgsqlRestClient/ExternalAuth.cs

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using static NpgsqlRestClient.Config;
1313
using static NpgsqlRestClient.Builder;
1414
using static NpgsqlRest.Auth.ClaimsDictionary;
15+
using NpgsqlRest.Auth;
1516

1617
namespace NpgsqlRestClient;
1718

@@ -41,8 +42,8 @@ public static class ExternalAuthConfig
4142
public static string ClientAnaliticsIpKey { get; private set; } = default!;
4243

4344
private static readonly Dictionary<string, ExternalAuthClientConfig> DefaultClientConfigs =
44-
new Dictionary<string, ExternalAuthClientConfig>
45-
{
45+
new()
46+
{
4647
{ "google", new ExternalAuthClientConfig
4748
{
4849
AuthUrl = "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id={0}&redirect_uri={1}&scope=openid profile email&state={2}",
@@ -190,7 +191,7 @@ public static void Configure(WebApplication app, NpgsqlRestOptions options, Post
190191
string code = (node["code"]?.ToString()) ??
191192
throw new ArgumentException("code retrieved from the external provider is null");
192193

193-
await ProcessAsync(code, body, config, context, options);
194+
await ProcessAsync(code, config, context, options);
194195
return;
195196
}
196197
catch (Exception e)
@@ -216,15 +217,14 @@ private static void PrepareResponse(string contentType, HttpContext context)
216217
context.Response.Headers.Expires = "0";
217218
}
218219

219-
private static readonly HttpClient _httpClient = new();
220+
private static HttpClient? _httpClient = null;
220221
private static readonly string _agent = $"{Guid.NewGuid().ToString()[..8]}";
221222

222223
private const string browserSessionStateKey = "__external_state";
223224
private const string browserSessionParamsKey = "__external_params";
224225

225226
private static async Task ProcessAsync(
226227
string code,
227-
string body,
228228
ExternalAuthClientConfig config,
229229
HttpContext context,
230230
NpgsqlRestOptions options)
@@ -233,6 +233,8 @@ private static async Task ProcessAsync(
233233
string? name;
234234
string token;
235235

236+
_httpClient ??= new();
237+
236238
using var requestTokenMessage = new HttpRequestMessage(HttpMethod.Post, config.TokenUrl);
237239
requestTokenMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(Application.Json));
238240
requestTokenMessage.Content = new FormUrlEncodedContent(new Dictionary<string, string>
@@ -273,6 +275,7 @@ private static async Task ProcessAsync(
273275
infoRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
274276
using var infoResponse = await _httpClient.SendAsync(infoRequest);
275277
var infoContent = await infoResponse.Content.ReadAsStringAsync();
278+
JsonNode infoNode;
276279
if (!infoResponse.IsSuccessStatusCode)
277280
{
278281
throw new HttpRequestException($"Info endpoint {config.InfoUrl} returned {infoResponse.StatusCode} with following content: {infoContent}");
@@ -284,18 +287,18 @@ private static async Task ProcessAsync(
284287

285288
try
286289
{
287-
JsonNode node = JsonNode.Parse(infoContent) ??
290+
infoNode = JsonNode.Parse(infoContent) ??
288291
throw new ArgumentException("info json node is null");
289292

290293
// Email parsing
291-
email = node["email"]?.ToString() ?? // Google, GitHub, Facebook, Microsoft
292-
node["userPrincipalName"]?.ToString() ?? // Microsoft (organizational accounts)
293-
node?["elements"]?[0]?["handle~"]?["emailAddress"]?.ToString(); // LinkedIn
294+
email = infoNode["email"]?.ToString() ?? // Google, GitHub, Facebook, Microsoft
295+
infoNode["userPrincipalName"]?.ToString() ?? // Microsoft (organizational accounts)
296+
infoNode?["elements"]?[0]?["handle~"]?["emailAddress"]?.ToString(); // LinkedIn
294297

295298
#pragma warning disable CS8602 // Dereference of a possibly null reference.
296-
name = node["localizedLastName"] is not null ?
297-
$"{node["localizedFirstName"]} {node["localizedLastName"]}".Trim() : // linkedin format
298-
node["name"]?.ToString(); // normal format
299+
name = infoNode["localizedLastName"] is not null ?
300+
$"{infoNode["localizedFirstName"]} {infoNode["localizedLastName"]}".Trim() : // linkedin format
301+
infoNode["name"]?.ToString(); // normal format
299302
#pragma warning restore CS8602 // Dereference of a possibly null reference.
300303
}
301304
catch (Exception e)
@@ -323,6 +326,7 @@ private static async Task ProcessAsync(
323326
{
324327
JsonNode node = JsonNode.Parse(emailContent) ??
325328
throw new ArgumentException("email json node is null");
329+
infoNode["emailRequest"] = node;
326330

327331
email = node["email"]?.ToString() ??
328332
node?["elements"]?[0]?["handle~"]?["emailAddress"]?.ToString(); // linkedin format
@@ -351,8 +355,8 @@ private static async Task ProcessAsync(
351355
await connection.OpenAsync();
352356
using var command = connection.CreateCommand();
353357
command.CommandText = ExternalAuthConfig.LoginCommand;
354-
var paramCount = command.CommandText[command.CommandText.IndexOf(Consts.OpenParenthesis)..].Split(Consts.Comma).Length;
355358

359+
var paramCount = PostgreSqlParameterCounter.CountParameters(command.CommandText);
356360
if (paramCount >= 1) command.Parameters.Add(new NpgsqlParameter()
357361
{
358362
Value = config.ExternalType,
@@ -368,9 +372,11 @@ private static async Task ProcessAsync(
368372
Value = name is not null ? name : DBNull.Value,
369373
NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Text
370374
});
375+
//emailContent
376+
371377
if (paramCount >= 4) command.Parameters.Add(new NpgsqlParameter()
372378
{
373-
Value = body is not null ? body : DBNull.Value,
379+
Value = infoNode is not null ? infoContent.ToString() : DBNull.Value,
374380
NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Json
375381
});
376382
if (paramCount >= 5)

NpgsqlRestClient/NpgsqlRestClient.csproj

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<InvariantGlobalization>true</InvariantGlobalization>
99
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
1010
<PublishAot>true</PublishAot>
11-
<Version>2.18.0</Version>
11+
<Version>2.19.0</Version>
1212
</PropertyGroup>
1313

1414
<ItemGroup>
@@ -22,5 +22,9 @@
2222
<ProjectReference Include="..\plugins\NpgsqlRest.HttpFiles\NpgsqlRest.HttpFiles.csproj" />
2323
<ProjectReference Include="..\plugins\NpgsqlRest.TsClient\NpgsqlRest.TsClient.csproj" />
2424
</ItemGroup>
25+
26+
<ItemGroup>
27+
<Folder Include="wwwroot\" />
28+
</ItemGroup>
2529

2630
</Project>

NpgsqlRestClient/Program.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
Configure(app, () =>
3838
{
3939
sw.Stop();
40-
var message = GetConfigStr("StartupMessage", Cfg);
40+
var message = GetConfigStr("StartupMessage", Cfg) ?? "Started in {0}, listening on {1}, version {2}";
4141
if (string.IsNullOrEmpty(message) is false)
4242
{
4343
Logger?.Information(message,
@@ -115,7 +115,8 @@
115115

116116
AuthenticationOptions = new()
117117
{
118-
DefaultAuthenticationType = GetConfigStr("DefaultAuthenticationType", AuthCfg)
118+
DefaultAuthenticationType = GetConfigStr("DefaultAuthenticationType", AuthCfg) ?? GetConfigStr("ApplicationName", Cfg),
119+
PasswordVerificationFailedCommand = GetConfigStr("PasswordVerificationFailedCommand", AuthCfg),
119120
},
120121

121122
EndpointCreateHandlers = CreateCodeGenHandlers(connectionString),

0 commit comments

Comments
 (0)