Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Npgsql.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=NOEXPORT/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Npgsql_0027s/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=pgpass/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=PGTZ/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgis/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Postgre/@EntryIndexedValue">True</s:Boolean>
Expand Down
7 changes: 1 addition & 6 deletions src/Npgsql/NpgsqlConnector.Auth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,7 @@ class AuthenticationCompleteException : Exception { }
if (password != null)
return password;

var passFile = Settings.Passfile ?? PostgresEnvironment.PassFile;
if (passFile is null && PostgresEnvironment.PassFileDefault is string passFileDefault)
{
passFile = passFileDefault;
}

var passFile = Settings.Passfile ?? PostgresEnvironment.PassFile ?? PostgresEnvironment.PassFileDefault;
if (passFile != null)
{
var matchingEntry = new PgPassFile(passFile!)
Expand Down
4 changes: 2 additions & 2 deletions src/Npgsql/NpgsqlConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,7 +644,7 @@ string GetUsername()
if (username?.Length > 0)
return username;

if (!PGUtil.IsWindows)
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
username = KerberosUsernameProvider.GetUsername(Settings.IncludeRealm);
if (username?.Length > 0)
Expand Down Expand Up @@ -716,7 +716,7 @@ async Task RawOpen(NpgsqlTimeout timeout, bool async, CancellationToken cancella
{
#if NET5_0
// It's PEM time
var keyPath = Settings.SslKey ?? PostgresEnvironment.SslKey;
var keyPath = Settings.SslKey ?? PostgresEnvironment.SslKey ?? PostgresEnvironment.SslKeyDefault;
cert = string.IsNullOrEmpty(password)
? X509Certificate2.CreateFromPemFile(certPath, keyPath)
: X509Certificate2.CreateFromEncryptedPemFile(certPath, password, keyPath);
Expand Down
38 changes: 27 additions & 11 deletions src/Npgsql/PostgresEnvironment.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Npgsql.Util;

namespace Npgsql
Expand All @@ -13,17 +14,28 @@ static class PostgresEnvironment

internal static string? PassFile => Environment.GetEnvironmentVariable("PGPASSFILE");

internal static string? PassFileDefault => GetDefaultFilePath(PGUtil.IsWindows ? "pgpass.conf" : ".pgpass");
internal static string? PassFileDefault
=> (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? Path.Combine(GetHomePostgresDir(), "pgpass.conf")
: Path.Combine(GetHomeDir(), ".pgpass")) is var path &&
File.Exists(path)
? path
: null;

internal static string? SslCert => Environment.GetEnvironmentVariable("PGSSLCERT");

internal static string? SslCertDefault => GetDefaultFilePath("postgresql.crt");
internal static string? SslCertDefault
=> Path.Combine(GetHomePostgresDir(), "postgresql.crt") is var path && File.Exists(path) ? path : null;

internal static string? SslCertRoot => Environment.GetEnvironmentVariable("PGSSLROOTCERT");
internal static string? SslKey => Environment.GetEnvironmentVariable("PGSSLKEY");

internal static string? SslCertRootDefault => GetDefaultFilePath("root.crt");
internal static string? SslKeyDefault
=> Path.Combine(GetHomePostgresDir(), "postgresql.key") is var path && File.Exists(path) ? path : null;

internal static string? SslKey => Environment.GetEnvironmentVariable("PGSSLKEY");
internal static string? SslCertRoot => Environment.GetEnvironmentVariable("PGSSLROOTCERT");

internal static string? SslCertRootDefault
=> Path.Combine(GetHomePostgresDir(), "root.crt") is var path && File.Exists(path) ? path : null;

internal static string? ClientEncoding => Environment.GetEnvironmentVariable("PGCLIENTENCODING");

Expand All @@ -33,11 +45,15 @@ static class PostgresEnvironment

internal static string? TargetSessionAttributes => Environment.GetEnvironmentVariable("PGTARGETSESSIONATTRS");

static string? GetDefaultFilePath(string fileName) =>
Environment.GetEnvironmentVariable(PGUtil.IsWindows ? "APPDATA" : "HOME") is string appData &&
Path.Combine(appData, "postgresql", fileName) is string filePath &&
File.Exists(filePath)
? filePath
: null;
static string GetHomeDir()
{
var envVar = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "APPDATA" : "HOME";
return Environment.GetEnvironmentVariable(envVar) is string homedir
? homedir
: throw new InvalidOperationException($"Environment variable {envVar} not defined");
}

static string GetHomePostgresDir()
=> Path.Combine(GetHomeDir(), RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "postgresql" : ".postgresql");
}
}
3 changes: 0 additions & 3 deletions src/Npgsql/Util/PGUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,6 @@ internal static int RotateShift(int val, int shift)
internal static readonly Task<bool> FalseTask = Task.FromResult(false);

internal static StringComparer InvariantCaseIgnoringStringComparer => StringComparer.InvariantCultureIgnoreCase;

internal static bool IsWindows =>
System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows);
}

enum FormatCode : short
Expand Down
89 changes: 79 additions & 10 deletions test/Npgsql.Tests/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1401,30 +1401,99 @@ public async Task NoResetOnClose(bool noResetOnClose)
}

[Test]
[NonParallelizable]
public async Task UsePgPassFile()
public async Task Use_pgpass_from_connection_string()
{
using var resetPassword = SetEnvironmentVariable("PGPASSWORD", null);
var builder = new NpgsqlConnectionStringBuilder(ConnectionString);

var password = builder.Password;
var passFile = Path.GetTempFileName();
builder.Password = null;

builder.Password = password;
var passFile = Path.GetTempFileName();
File.WriteAllText(passFile, $"*:*:*:{builder.Username}:{password}");
builder.Passfile = passFile;

using var deletePassFile = Defer(() => File.Delete(passFile));
try
{
using var pool = CreateTempPool(builder.ConnectionString, out var connectionString);
using var conn = await OpenConnectionAsync(connectionString);
}
finally
{
File.Delete(passFile);
}
}

File.WriteAllText(passFile, $"*:*:*:{builder.Username}:{password}");
[Test]
[NonParallelizable]
public async Task Use_pgpass_from_environment_variable()
{
using var resetPassword = SetEnvironmentVariable("PGPASSWORD", null);
var builder = new NpgsqlConnectionStringBuilder(ConnectionString);

var password = builder.Password;
builder.Password = null;

var passFile = Path.GetTempFileName();
File.WriteAllText(passFile, $"*:*:*:{builder.Username}:{password}");
using var passFileVariable = SetEnvironmentVariable("PGPASSFILE", passFile);
using var pool = CreateTempPool(builder.ConnectionString, out var connectionString);
using var conn = await OpenConnectionAsync(connectionString);

try
{
using var pool = CreateTempPool(builder.ConnectionString, out var connectionString);
using var conn = await OpenConnectionAsync(connectionString);
}
finally
{
File.Delete(passFile);
}
}

[Test]
[NonParallelizable]
public async Task Use_pgpass_from_homedir()
{
using var resetPassword = SetEnvironmentVariable("PGPASSWORD", null);
var builder = new NpgsqlConnectionStringBuilder(ConnectionString);

var password = builder.Password;
builder.Password = null;

string? dirToDelete = null;
string passFile;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var dir = Path.Combine(Environment.GetEnvironmentVariable("APPDATA")!, "postgresql");
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
dirToDelete = dir;

}
passFile = Path.Combine(dir, "pgpass.conf");
}
else
{
passFile = Path.Combine(Environment.GetEnvironmentVariable("HOME")!, ".pgpass");
}

try
{
File.WriteAllText(passFile, $"*:*:*:{builder.Username}:{password}");
using var pool = CreateTempPool(builder.ConnectionString, out var connectionString);
using var conn = await OpenConnectionAsync(connectionString);
}
finally
{
File.Delete(passFile);
if (dirToDelete is not null)
Directory.Delete(dirToDelete);
}
}

[Test]
[NonParallelizable]
public void PasswordSourcePrecendence()
public void PasswordSourcePrecedence()
{
using var resetPassword = SetEnvironmentVariable("PGPASSWORD", null);
var builder = new NpgsqlConnectionStringBuilder(ConnectionString);
Expand Down Expand Up @@ -1464,7 +1533,7 @@ Func<ValueTask> OpenConnection(string? password, string? passFile) => async () =
builder.Password = password;
builder.Passfile = passFile;
builder.IntegratedSecurity = false;
builder.ApplicationName = $"{nameof(PasswordSourcePrecendence)}:{Guid.NewGuid()}";
builder.ApplicationName = $"{nameof(PasswordSourcePrecedence)}:{Guid.NewGuid()}";

using var pool = CreateTempPool(builder.ConnectionString, out var connectionString);
using var connection = await OpenConnectionAsync(connectionString);
Expand Down