/
dev-npgsql
/
npgsql
Обзор
Документация
Войти
/
dev-npgsql
/
npgsql
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v6.0.13
src/Npgsql/PgPassFile.cs
159 строк
7 KB
Shay Rojansky
Apply file-scoped namespaces for 6.0 (#4298)
28 янв 2022, 15:13
Не верифицирован
28 янв 2022, 15:13
2e0ad2d
Код
Авторство
О чём код?
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.RegularExpressions; namespace Npgsql; /// <summary> /// Represents a .pgpass file, which contains passwords for noninteractive connections /// </summary> class PgPassFile { #region Properties /// <summary> /// File name being parsed for credentials /// </summary> internal string FileName { get; } #endregion #region Construction /// <summary> /// Initializes a new instance of the <see cref="PgPassFile"/> class /// </summary> /// <param name="fileName"></param> public PgPassFile(string fileName) => FileName = fileName; #endregion /// <summary> /// Parses file content and gets all credentials from the file /// </summary> /// <returns><see cref="IEnumerable{PgPassEntry}"/> corresponding to all lines in the .pgpass file</returns> internal IEnumerable<Entry> Entries => File.ReadLines(FileName) .Select(line => line.Trim()) .Where(line => line.Any() && line[0] != '#') .Select(Entry.Parse); /// <summary> /// Searches queries loaded from .PGPASS file to find first entry matching the provided parameters. /// </summary> /// <param name="host">Hostname to query. Use null to match any.</param> /// <param name="port">Port to query. Use null to match any.</param> /// <param name="database">Database to query. Use null to match any.</param> /// <param name="username">User name to query. Use null to match any.</param> /// <returns>Matching <see cref="Entry"/> if match was found. Otherwise, returns null.</returns> internal Entry? GetFirstMatchingEntry(string? host = null, int? port = null, string? database = null, string? username = null) => Entries.FirstOrDefault(entry => entry.IsMatch(host, port, database, username)); /// <summary> /// Represents a hostname, port, database, username, and password combination that has been retrieved from a .pgpass file /// </summary> internal class Entry { const string PgPassWildcard = "*"; #region Fields and Properties /// <summary> /// Hostname parsed from the .pgpass file /// </summary> internal string? Host { get; } /// <summary> /// Port parsed from the .pgpass file /// </summary> internal int? Port { get; } /// <summary> /// Database parsed from the .pgpass file /// </summary> internal string? Database { get; } /// <summary> /// User name parsed from the .pgpass file /// </summary> internal string? Username { get; } /// <summary> /// Password parsed from the .pgpass file /// </summary> internal string? Password { get; } #endregion #region Construction / Initialization /// <summary> /// This class represents an entry from the .pgpass file /// </summary> /// <param name="host">Hostname parsed from the .pgpass file</param> /// <param name="port">Port parsed from the .pgpass file</param> /// <param name="database">Database parsed from the .pgpass file</param> /// <param name="username">User name parsed from the .pgpass file</param> /// <param name="password">Password parsed from the .pgpass file</param> Entry(string? host, int? port, string? database, string? username, string? password) { Host = host; Port = port; Database = database; Username = username; Password = password; } /// <summary> /// Creates new <see cref="Entry"/> based on string in the format hostname:port:database:username:password. The : and \ characters should be escaped with a \. /// </summary> /// <param name="serializedEntry">string for the entry from the pgpass file</param> /// <returns>New instance of <see cref="Entry"/> for the string</returns> /// <exception cref="FormatException">Entry is not formatted as hostname:port:database:username:password or non-wildcard port is not a number</exception> internal static Entry Parse(string serializedEntry) { var parts = Regex.Split(serializedEntry, @"(?<!\\):"); // split on any colons that aren't preceded by a \ (\ indicates that the colon is part of the content and not a separator) if (parts is null || parts.Length != 5) throw new FormatException("pgpass entry was not well-formed. Please ensure all non-comment entries are formatted as hostname:port:database:username:password. If colon is included, it must be escaped like \\:."); var processedParts = parts .Select(part => part.Replace("\\:", ":").Replace("\\\\", "\\")) // unescape any escaped characters .Select(part => part == PgPassWildcard ? null : part) .ToArray(); int? port = null; if (processedParts[1] != null) { if (!int.TryParse(processedParts[1], out var tempPort)) throw new FormatException("pgpass entry was not formatted correctly. Port must be a valid integer."); port = tempPort; } return new Entry(processedParts[0], port, processedParts[2], processedParts[3], processedParts[4]); } #endregion /// <summary> /// Checks whether this <see cref="Entry"/> matches the parameters supplied /// </summary> /// <param name="host">Hostname to check against this entry</param> /// <param name="port">Port to check against this entry</param> /// <param name="database">Database to check against this entry</param> /// <param name="username">Username to check against this entry</param> /// <returns>True if the entry is a match. False otherwise.</returns> internal bool IsMatch(string? host, int? port, string? database, string? username) => AreValuesMatched(host, Host) && AreValuesMatched(port, Port) && AreValuesMatched(database, Database) && AreValuesMatched(username, Username); /// <summary> /// Checks if 2 strings are a match for a <see cref="Entry"/> considering that either value can be a wildcard (*) /// </summary> /// <param name="query">Value being searched</param> /// <param name="actual">Value from the PGPASS entry</param> /// <returns>True if the values are a match. False otherwise.</returns> bool AreValuesMatched(string? query, string? actual) => query == actual || actual == null || query == null; bool AreValuesMatched(int? query, int? actual) => query == actual || actual == null || query == null; } }