public inbox for [email protected]
help / color / mirror / Atom feedFrom: Julien Rouhaud <[email protected]>
Subject: [PATCH v2 3/4] Allow file inclusion in pg_hba and pg_ident files.
Date: Mon, 21 Feb 2022 15:45:26 +0800
Author: Julien Rouhaud
Reviewed-by: FIXME
Discussion: https://postgr.es/m/20220223045959.35ipdsvbxcstrhya%40jrouhaud
---
doc/src/sgml/catalogs.sgml | 48 +++-
doc/src/sgml/client-auth.sgml | 34 ++-
src/backend/libpq/hba.c | 373 ++++++++++++++++---------
src/backend/libpq/pg_hba.conf.sample | 8 +-
src/backend/libpq/pg_ident.conf.sample | 8 +-
src/backend/utils/adt/hbafuncs.c | 53 +++-
src/include/catalog/pg_proc.dat | 12 +-
src/include/libpq/hba.h | 2 +
src/test/regress/expected/rules.out | 12 +-
9 files changed, 395 insertions(+), 155 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 0de40a9626..07c6679a52 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -10430,12 +10430,31 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</thead>
<tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>rule_number</structfield> <type>int4</type>
+ </para>
+ <para>
+ Rule number, in priority order, of this rule if the rule is valid,
+ otherwise null
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>file_name</structfield> <type>text</type>
+ </para>
+ <para>
+ File name of this rule
+ </para></entry>
+ </row>
+
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>line_number</structfield> <type>int4</type>
</para>
<para>
- Line number of this rule in <filename>pg_hba.conf</filename>
+ Line number of this rule in the given file_name
</para></entry>
</row>
@@ -10571,6 +10590,33 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
</thead>
<tbody>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>mapping_number</structfield> <type>int4</type>
+ </para>
+ <para>
+ Rule number, in priority order, of this mapping if the mapping is valid,
+ otherwise null
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>file_name</structfield> <type>text</type>
+ </para>
+ <para>
+ File name of this mapping
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>line_number</structfield> <type>int4</type>
+ </para>
+ <para>
+ Line number of this mapping in the given file_name
+ </para></entry>
+ </row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>line_number</structfield> <type>int4</type>
diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 142b0affcb..e1d0e103b3 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -89,8 +89,17 @@
</para>
<para>
- Each record specifies a connection type, a client IP address range
- (if relevant for the connection type), a database name, a user name,
+ Each record can either be an inclusion directive or an authentication rule.
+ Inclusion records specifies files that can be included, which contains
+ additional records. The records will be inserted in lieu of the inclusion
+ records. Those records only contains two fields: the
+ <literal>include</literal> directive and the file to be included. The file
+ can be a relative of absolute path, and can be double quoted if needed.
+ </para>
+
+ <para>
+ Each authentication record specifies a connection type, a client IP address
+ range (if relevant for the connection type), a database name, a user name,
and the authentication method to be used for connections matching
these parameters. The first record with a matching connection type,
client address, requested database, and user name is used to perform
@@ -103,6 +112,7 @@
<para>
A record can have several formats:
<synopsis>
+include <replaceable>file</replaceable>
local <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
host <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
hostssl <replaceable>database</replaceable> <replaceable>user</replaceable> <replaceable>address</replaceable> <replaceable>auth-method</replaceable> <optional><replaceable>auth-options</replaceable></optional>
@@ -118,6 +128,15 @@ hostnogssenc <replaceable>database</replaceable> <replaceable>user</replaceabl
The meaning of the fields is as follows:
<variablelist>
+ <varlistentry>
+ <term><literal>include</literal></term>
+ <listitem>
+ <para>
+ This line will be replaced with the content of the given file.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><literal>local</literal></term>
<listitem>
@@ -835,8 +854,9 @@ local db1,db2,@demodbs all md5
cluster's data directory. (It is possible to place the map file
elsewhere, however; see the <xref linkend="guc-ident-file"/>
configuration parameter.)
- The ident map file contains lines of the general form:
+ The ident map file contains lines of two general form:
<synopsis>
+<replaceable>include</replaceable> <replaceable>file</replaceable>
<replaceable>map-name</replaceable> <replaceable>system-username</replaceable> <replaceable>database-username</replaceable>
</synopsis>
Comments, whitespace and line continuations are handled in the same way as in
@@ -847,6 +867,14 @@ local db1,db2,@demodbs all md5
database user name. The same <replaceable>map-name</replaceable> can be
used repeatedly to specify multiple user-mappings within a single map.
</para>
+ <para>
+ The lines can record can either be an inclusion directive or an authentication rule.
+ Inclusion records specifies files that can be included, which contains
+ additional records. The records will be inserted in lieu of the inclusion
+ records. Those records only contains two fields: the
+ <literal>include</literal> directive and the file to be included. The file
+ can be a relative of absolute path, and can be double quoted if needed.
+ </para>
<para>
There is no restriction regarding how many database users a given
operating system user can correspond to, nor vice versa. Thus, entries
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index e673ff5474..85988fdeda 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -68,6 +68,12 @@ typedef struct check_network_data
#define token_is_keyword(t, k) (!t->quoted && strcmp(t->string, k) == 0)
#define token_matches(t, k) (strcmp(t->string, k) == 0)
+typedef enum HbaIncludeKind
+{
+ SecondaryAuthFile,
+ IncludedAuthFile
+} HbaIncludeKind;
+
/*
* pre-parsed content of HBA config file: list of HbaLine structs.
* parsed_hba_context is the memory context where it lives.
@@ -112,6 +118,12 @@ static const char *const UserAuthName[] =
};
+static FILE *open_inc_file(HbaIncludeKind kind, const char *inc_filename,
+ const char *outer_filename, int elevel,
+ char **err_msg, char **inc_fullname);
+static void tokenize_auth_file_with_context(MemoryContext Linecxt,
+ const char *filename, FILE *file,
+ List **tok_lines, int elevel);
static List *tokenize_inc_file(List *tokens, const char *outer_filename,
const char *inc_filename, int elevel, char **err_msg);
static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
@@ -327,6 +339,68 @@ next_field_expand(const char *filename, char **lineptr,
return tokens;
}
+/*
+ * Open the given file for inclusion in an authentication file, whether
+ * secondary or included.
+*/
+static FILE *
+open_inc_file(HbaIncludeKind kind, const char *inc_filename,
+ const char *outer_filename, int elevel, char **err_msg,
+ char **inc_fullname)
+{
+ FILE *inc_file;
+
+ if (is_absolute_path(inc_filename))
+ {
+ /* absolute path is taken as-is */
+ *inc_fullname = pstrdup(inc_filename);
+ }
+ else
+ {
+ /* relative path is relative to dir of calling file */
+ *inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
+ strlen(inc_filename) + 1);
+ strcpy(*inc_fullname, outer_filename);
+ get_parent_directory(*inc_fullname);
+ join_path_components(*inc_fullname, *inc_fullname, inc_filename);
+ canonicalize_path(*inc_fullname);
+ }
+
+ inc_file = AllocateFile(*inc_fullname, "r");
+ if (inc_file == NULL)
+ {
+ int save_errno = errno;
+ const char *msglog;
+ const char *msgview;
+
+ switch (kind)
+ {
+ case SecondaryAuthFile:
+ msglog = "could not open secondary authentication file \"@%s\" as \"%s\": %m";
+ msgview = "could not open secondary authentication file \"@%s\" as \"%s\": %s";
+ break;
+ case IncludedAuthFile:
+ msglog = "could not open included authentication file \"%s\" as \"%s\": %m";
+ msgview = "could not open included authentication file \"%s\" as \"%s\": %s";
+ break;
+ default:
+ elog(ERROR, "unknown HbaIncludeKind: %d", kind);
+ break;
+ }
+
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg(msglog, inc_filename, *inc_fullname)));
+ *err_msg = psprintf(msgview, inc_filename, *inc_fullname,
+ strerror(save_errno));
+ pfree(*inc_fullname);
+ *inc_fullname = NULL;
+ return NULL;
+ }
+
+ return inc_file;
+}
+
/*
* tokenize_inc_file
* Expand a file included from another file into an hba "field"
@@ -355,36 +429,11 @@ tokenize_inc_file(List *tokens,
ListCell *inc_line;
MemoryContext linecxt;
- if (is_absolute_path(inc_filename))
- {
- /* absolute path is taken as-is */
- inc_fullname = pstrdup(inc_filename);
- }
- else
- {
- /* relative path is relative to dir of calling file */
- inc_fullname = (char *) palloc(strlen(outer_filename) + 1 +
- strlen(inc_filename) + 1);
- strcpy(inc_fullname, outer_filename);
- get_parent_directory(inc_fullname);
- join_path_components(inc_fullname, inc_fullname, inc_filename);
- canonicalize_path(inc_fullname);
- }
+ inc_file = open_inc_file(SecondaryAuthFile, inc_filename, outer_filename,
+ elevel, err_msg, &inc_fullname);
- inc_file = AllocateFile(inc_fullname, "r");
if (inc_file == NULL)
- {
- int save_errno = errno;
-
- ereport(elevel,
- (errcode_for_file_access(),
- errmsg("could not open secondary authentication file \"@%s\" as \"%s\": %m",
- inc_filename, inc_fullname)));
- *err_msg = psprintf("could not open secondary authentication file \"@%s\" as \"%s\": %s",
- inc_filename, inc_fullname, strerror(save_errno));
- pfree(inc_fullname);
return tokens;
- }
/* There is possible recursion here if the file contains @ */
linecxt = tokenize_auth_file(inc_fullname, inc_file, &inc_lines, elevel);
@@ -423,6 +472,169 @@ tokenize_inc_file(List *tokens,
return tokens;
}
+/*
+ * Tokenize the given file.
+ *
+ * The output is a list of TokenizedAuthLine structs; see struct definition
+ * above.
+ *
+ * linecxt: memory context which must contain all memory allocated by the
+ * function
+ * filename: the absolute path to the target file
+ * file: the already-opened target file
+ * tok_lines: receives output list
+ * elevel: message logging level
+ *
+ * Errors are reported by logging messages at ereport level elevel and by
+ * adding TokenizedAuthLine structs containing non-null err_msg fields to the
+ * output list.
+ *
+ */
+static void
+tokenize_auth_file_with_context(MemoryContext linecxt, const char *filename,
+ FILE *file, List **tok_lines, int elevel)
+{
+ StringInfoData buf;
+ int line_number = 1;
+ MemoryContext oldcxt;
+
+ oldcxt = MemoryContextSwitchTo(linecxt);
+
+ initStringInfo(&buf);
+
+ while (!feof(file) && !ferror(file))
+ {
+ TokenizedAuthLine *tok_line;
+ char *lineptr;
+ List *current_line = NIL;
+ char *err_msg = NULL;
+ int last_backslash_buflen = 0;
+ int continuations = 0;
+
+ /* Collect the next input line, handling backslash continuations */
+ resetStringInfo(&buf);
+
+ while (pg_get_line_append(file, &buf, NULL))
+ {
+ /* Strip trailing newline, including \r in case we're on Windows */
+ buf.len = pg_strip_crlf(buf.data);
+
+ /*
+ * Check for backslash continuation. The backslash must be after
+ * the last place we found a continuation, else two backslashes
+ * followed by two \n's would behave surprisingly.
+ */
+ if (buf.len > last_backslash_buflen &&
+ buf.data[buf.len - 1] == '\\')
+ {
+ /* Continuation, so strip it and keep reading */
+ buf.data[--buf.len] = '\0';
+ last_backslash_buflen = buf.len;
+ continuations++;
+ continue;
+ }
+
+ /* Nope, so we have the whole line */
+ break;
+ }
+
+ if (ferror(file))
+ {
+ /* I/O error! */
+ int save_errno = errno;
+
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+ err_msg = psprintf("could not read file \"%s\": %s",
+ filename, strerror(save_errno));
+ break;
+ }
+
+ /* Parse fields */
+ lineptr = buf.data;
+ while (*lineptr && err_msg == NULL)
+ {
+ List *current_field;
+
+ current_field = next_field_expand(filename, &lineptr,
+ elevel, &err_msg);
+ /* add field to line, unless we are at EOL or comment start */
+ if (current_field != NIL)
+ current_line = lappend(current_line, current_field);
+ }
+
+ /*
+ * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
+ * boring.
+ */
+ if (current_line == NIL && err_msg == NULL)
+ goto next_line;
+
+ /* If the line is valid, check if that's an include directive */
+ if (err_msg == NULL && list_length(current_line) == 2)
+ {
+ HbaToken *first, *second;
+
+ first = linitial(linitial_node(List, current_line));
+ second = linitial(lsecond_node(List, current_line));
+
+ if (strcmp(first->string, "include") == 0)
+ {
+ char *inc_filename;
+ char *inc_fullname;
+ FILE *inc_file;
+
+ inc_filename = second->string;
+
+ inc_file = open_inc_file(IncludedAuthFile, inc_filename,
+ filename, elevel, &err_msg,
+ &inc_fullname);
+
+ /*
+ * The included file could be open, now recursively process it.
+ * Errors will be reported in the general TokenizedAuthLine
+ * processing.
+ */
+ if (inc_file != NULL)
+ {
+ tokenize_auth_file_with_context(linecxt, inc_fullname,
+ inc_file, tok_lines,
+ elevel);
+
+ FreeFile(inc_file);
+ pfree(inc_fullname);
+
+ /*
+ * The line is fulle processed, bypass the general
+ * TokenizedAuthLine processing.
+ */
+ goto next_line;
+ }
+ else
+ {
+ /* We should got an error */
+ Assert(err_msg != NULL);
+ }
+ }
+ }
+
+ /* General processing: Emit line to the TokenizedAuthLine */
+ tok_line = (TokenizedAuthLine *) palloc(sizeof(TokenizedAuthLine));
+ tok_line->fields = current_line;
+ tok_line->file_name = pstrdup(filename);
+ tok_line->line_num = line_number;
+ tok_line->raw_line = pstrdup(buf.data);
+ tok_line->err_msg = err_msg;
+ *tok_lines = lappend(*tok_lines, tok_line);
+
+next_line:
+ line_number += continuations + 1;
+
+ }
+
+ MemoryContextSwitchTo(oldcxt);
+}
/*
* Does user belong to role?
@@ -1796,6 +2008,7 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
HbaLine *parsedline;
parsedline = palloc0(sizeof(HbaLine));
+ parsedline->sourcefile = pstrdup(tok_line->file_name);
parsedline->linenumber = line_num;
parsedline->rawline = pstrdup(tok_line->raw_line);
@@ -2602,118 +2815,26 @@ parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
/*
* Tokenize the given file.
*
- * The output is a list of TokenizedAuthLine structs; see struct definition
- * above.
+ * Wrapper around tokenize_auth_file_with_context, creating a dedicated memory
+ * context.
*
- * filename: the absolute path to the target file
- * file: the already-opened target file
- * tok_lines: receives output list
- * elevel: message logging level
- *
- * Errors are reported by logging messages at ereport level elevel and by
- * adding TokenizedAuthLine structs containing non-null err_msg fields to the
- * output list.
- *
- * Return value is a memory context which contains all memory allocated by
+ * Return value is this memory context which contains all memory allocated by
* this function (it's a child of caller's context).
*/
MemoryContext
-tokenize_auth_file(const char *filename, FILE *file, List **tok_lines, int elevel)
+tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
+ int elevel)
{
- int line_number = 1;
- StringInfoData buf;
MemoryContext linecxt;
- MemoryContext oldcxt;
linecxt = AllocSetContextCreate(CurrentMemoryContext,
"tokenize_auth_file",
ALLOCSET_SMALL_SIZES);
- oldcxt = MemoryContextSwitchTo(linecxt);
-
- initStringInfo(&buf);
*tok_lines = NIL;
- while (!feof(file) && !ferror(file))
- {
- char *lineptr;
- List *current_line = NIL;
- char *err_msg = NULL;
- int last_backslash_buflen = 0;
- int continuations = 0;
-
- /* Collect the next input line, handling backslash continuations */
- resetStringInfo(&buf);
-
- while (pg_get_line_append(file, &buf, NULL))
- {
- /* Strip trailing newline, including \r in case we're on Windows */
- buf.len = pg_strip_crlf(buf.data);
-
- /*
- * Check for backslash continuation. The backslash must be after
- * the last place we found a continuation, else two backslashes
- * followed by two \n's would behave surprisingly.
- */
- if (buf.len > last_backslash_buflen &&
- buf.data[buf.len - 1] == '\\')
- {
- /* Continuation, so strip it and keep reading */
- buf.data[--buf.len] = '\0';
- last_backslash_buflen = buf.len;
- continuations++;
- continue;
- }
-
- /* Nope, so we have the whole line */
- break;
- }
-
- if (ferror(file))
- {
- /* I/O error! */
- int save_errno = errno;
-
- ereport(elevel,
- (errcode_for_file_access(),
- errmsg("could not read file \"%s\": %m", filename)));
- err_msg = psprintf("could not read file \"%s\": %s",
- filename, strerror(save_errno));
- break;
- }
-
- /* Parse fields */
- lineptr = buf.data;
- while (*lineptr && err_msg == NULL)
- {
- List *current_field;
-
- current_field = next_field_expand(filename, &lineptr,
- elevel, &err_msg);
- /* add field to line, unless we are at EOL or comment start */
- if (current_field != NIL)
- current_line = lappend(current_line, current_field);
- }
-
- /*
- * Reached EOL; emit line to TokenizedAuthLine list unless it's boring
- * */
- if (current_line != NIL || err_msg != NULL)
- {
- TokenizedAuthLine *tok_line;
-
- tok_line = (TokenizedAuthLine *) palloc(sizeof(TokenizedAuthLine));
- tok_line->fields = current_line;
- tok_line->line_num = line_number;
- tok_line->raw_line = pstrdup(buf.data);
- tok_line->err_msg = err_msg;
- *tok_lines = lappend(*tok_lines, tok_line);
- }
-
- line_number += continuations + 1;
- }
-
- MemoryContextSwitchTo(oldcxt);
+ tokenize_auth_file_with_context(linecxt, filename, file, tok_lines,
+ elevel);
return linecxt;
}
diff --git a/src/backend/libpq/pg_hba.conf.sample b/src/backend/libpq/pg_hba.conf.sample
index 5f3f63eb0c..0b6589a7b9 100644
--- a/src/backend/libpq/pg_hba.conf.sample
+++ b/src/backend/libpq/pg_hba.conf.sample
@@ -9,6 +9,7 @@
# are authenticated, which PostgreSQL user names they can use, which
# databases they can access. Records take one of these forms:
#
+# include FILE
# local DATABASE USER METHOD [OPTIONS]
# host DATABASE USER ADDRESS METHOD [OPTIONS]
# hostssl DATABASE USER ADDRESS METHOD [OPTIONS]
@@ -18,7 +19,12 @@
#
# (The uppercase items must be replaced by actual values.)
#
-# The first field is the connection type:
+# If the first field is "include", it's not a mapping record but a directive to
+# include records from another file, specified in the field. FILE is the file
+# to include. It can be specified with a relative or absolute path, and can be
+# double quoted if it contains spaces.
+#
+# Otherwise the first field is the connection type:
# - "local" is a Unix-domain socket
# - "host" is a TCP/IP socket (encrypted or not)
# - "hostssl" is a TCP/IP socket that is SSL-encrypted
diff --git a/src/backend/libpq/pg_ident.conf.sample b/src/backend/libpq/pg_ident.conf.sample
index a5870e6448..138359cf03 100644
--- a/src/backend/libpq/pg_ident.conf.sample
+++ b/src/backend/libpq/pg_ident.conf.sample
@@ -7,12 +7,18 @@
#
# This file controls PostgreSQL user name mapping. It maps external
# user names to their corresponding PostgreSQL user names. Records
-# are of the form:
+# are one of these forms:
#
+# include FILE
# MAPNAME SYSTEM-USERNAME PG-USERNAME
#
# (The uppercase quantities must be replaced by actual values.)
#
+# If the first field is "include", it's not an authentication record but a
+# directive to include records from another file, specified in the field. FILE
+# is the file to include. It can be specified with a relative or absolute
+# path, and can be double quoted if it contains spaces.
+#
# MAPNAME is the (otherwise freely chosen) map name that was used in
# pg_hba.conf. SYSTEM-USERNAME is the detected user name of the
# client. PG-USERNAME is the requested PostgreSQL user name. The
diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c
index 2189c298b8..4c9df29427 100644
--- a/src/backend/utils/adt/hbafuncs.c
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -26,16 +26,18 @@
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
- int lineno, HbaLine *hba, const char *err_msg);
+ int tule_number, const char *filename, int lineno,
+ HbaLine *hba, const char *err_msg);
static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
+ int mappint_number, const char *filename,
int lineno, IdentLine *ident, const char *err_msg);
static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
static ArrayType *gethba_options(HbaLine *hba);
/* Number of columns in pg_hba_file_rules view */
-#define NUM_PG_HBA_FILE_RULES_ATTS 9
+#define NUM_PG_HBA_FILE_RULES_ATTS 11
/*
* fill_hba_line: build one row of pg_hba_file_rules view, add it to tuplestore
@@ -51,7 +53,8 @@ static ArrayType *gethba_options(HbaLine *hba);
*/
static void
fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
- int lineno, HbaLine *hba, const char *err_msg)
+ int rule_number, const char *filename, int lineno, HbaLine *hba,
+ const char *err_msg)
{
Datum values[NUM_PG_HBA_FILE_RULES_ATTS];
bool nulls[NUM_PG_HBA_FILE_RULES_ATTS];
@@ -70,6 +73,13 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
memset(nulls, 0, sizeof(nulls));
index = 0;
+ /* rule_number */
+ if (err_msg)
+ nulls[index++] = true;
+ else
+ values[index++] = Int32GetDatum(rule_number);
+ /* file_name */
+ values[index++] = CStringGetTextDatum(filename);
/* line_number */
values[index++] = Int32GetDatum(lineno);
@@ -213,7 +223,7 @@ fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
else
{
/* no parsing result, so set relevant fields to nulls */
- memset(&nulls[1], true, (NUM_PG_HBA_FILE_RULES_ATTS - 2) * sizeof(bool));
+ memset(&nulls[3], true, (NUM_PG_HBA_FILE_RULES_ATTS - 4) * sizeof(bool));
}
/* error */
@@ -235,6 +245,7 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
FILE *file;
List *hba_lines = NIL;
ListCell *line;
+ int rule_number = 0;
MemoryContext linecxt;
MemoryContext hbacxt;
MemoryContext oldcxt;
@@ -265,12 +276,15 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
HbaLine *hbaline = NULL;
- /* don't parse lines that already have errors */
+ /* Only parse lines that don't already have an error */
if (tok_line->err_msg == NULL)
+ {
hbaline = parse_hba_line(tok_line, DEBUG3);
+ rule_number++;
+ }
- fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
- hbaline, tok_line->err_msg);
+ fill_hba_line(tuple_store, tupdesc, rule_number, tok_line->file_name,
+ tok_line->line_num, hbaline, tok_line->err_msg);
}
/* Free tokenizer memory */
@@ -281,7 +295,7 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
}
/* Number of columns in pg_hba_file_mappings view */
-#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5
+#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 7
/*
* fill_ident_line: build one row of pg_ident_file_mappings view, add it to
@@ -298,7 +312,8 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
*/
static void
fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
- int lineno, IdentLine *ident, const char *err_msg)
+ int mapping_number, const char *filename, int lineno,
+ IdentLine *ident, const char *err_msg)
{
Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
@@ -311,6 +326,13 @@ fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
memset(nulls, 0, sizeof(nulls));
index = 0;
+ /* mapping_number */
+ if (err_msg)
+ nulls[index++] = true;
+ else
+ values[index++] = Int32GetDatum(mapping_number);
+ /* file_name */
+ values[index++] = CStringGetTextDatum(filename);
/* line_number */
values[index++] = Int32GetDatum(lineno);
@@ -323,7 +345,7 @@ fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
else
{
/* no parsing result, so set relevant fields to nulls */
- memset(&nulls[1], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 2) * sizeof(bool));
+ memset(&nulls[3], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 4) * sizeof(bool));
}
/* error */
@@ -345,6 +367,7 @@ fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
FILE *file;
List *ident_lines = NIL;
ListCell *line;
+ int mapping_number = 0;
MemoryContext linecxt;
MemoryContext identcxt;
MemoryContext oldcxt;
@@ -375,12 +398,16 @@ fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
IdentLine *identline = NULL;
- /* don't parse lines that already have errors */
+ /* Only parse lines that don't already have an error */
if (tok_line->err_msg == NULL)
+ {
identline = parse_ident_line(tok_line, DEBUG3);
+ mapping_number++;
+ }
- fill_ident_line(tuple_store, tupdesc, tok_line->line_num, identline,
- tok_line->err_msg);
+ fill_ident_line(tuple_store, tupdesc, mapping_number,
+ tok_line->file_name, tok_line->line_num, identline,
+ tok_line->err_msg);
}
/* Free tokenizer memory */
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 04d4edb228..e3baafc3e3 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6116,16 +6116,16 @@
{ oid => '3401', descr => 'show pg_hba.conf rules',
proname => 'pg_hba_file_rules', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
- proallargtypes => '{int4,text,_text,_text,text,text,text,_text,text}',
- proargmodes => '{o,o,o,o,o,o,o,o,o}',
- proargnames => '{line_number,type,database,user_name,address,netmask,auth_method,options,error}',
+ proallargtypes => '{int4,text,int4,text,_text,_text,text,text,text,_text,text}',
+ proargmodes => '{o,o,o,o,o,o,o,o,o,o,o}',
+ proargnames => '{rule_number,file_name,line_number,type,database,user_name,address,netmask,auth_method,options,error}',
prosrc => 'pg_hba_file_rules' },
{ oid => '9556', descr => 'show pg_ident.conf mappings',
proname => 'pg_ident_file_mappings', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
- proallargtypes => '{int4,text,text,text,text}',
- proargmodes => '{o,o,o,o,o}',
- proargnames => '{line_number,map_name,sys_name,pg_usernamee,error}',
+ proallargtypes => '{int4,text,int4,text,text,text,text}',
+ proargmodes => '{o,o,o,o,o,o,o}',
+ proargnames => '{mapping_number,file_name,line_number,map_name,sys_name,pg_usernamee,error}',
prosrc => 'pg_ident_file_mappings' },
{ oid => '1371', descr => 'view system lock information',
proname => 'pg_lock_status', prorows => '1000', proretset => 't',
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index fce7db248b..551e961585 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -79,6 +79,7 @@ typedef enum ClientCertName
typedef struct HbaLine
{
+ char *sourcefile;
int linenumber;
char *rawline;
ConnType conntype;
@@ -153,6 +154,7 @@ typedef struct IdentLine
typedef struct TokenizedAuthLine
{
List *fields; /* List of lists of HbaTokens */
+ char *file_name; /* File name */
int line_num; /* Line number */
char *raw_line; /* Raw line text */
char *err_msg; /* Error message if any */
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 62cf0d8674..e6f274cb59 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1337,7 +1337,9 @@ pg_group| SELECT pg_authid.rolname AS groname,
WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist
FROM pg_authid
WHERE (NOT pg_authid.rolcanlogin);
-pg_hba_file_rules| SELECT a.line_number,
+pg_hba_file_rules| SELECT a.rule_number,
+ a.file_name,
+ a.line_number,
a.type,
a.database,
a.user_name,
@@ -1346,13 +1348,15 @@ pg_hba_file_rules| SELECT a.line_number,
a.auth_method,
a.options,
a.error
- FROM pg_hba_file_rules() a(line_number, type, database, user_name, address, netmask, auth_method, options, error);
-pg_ident_file_mappings| SELECT a.line_number,
+ FROM pg_hba_file_rules() a(rule_number, file_name, line_number, type, database, user_name, address, netmask, auth_method, options, error);
+pg_ident_file_mappings| SELECT a.mapping_number,
+ a.file_name,
+ a.line_number,
a.map_name,
a.sys_name,
a.pg_usernamee,
a.error
- FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_usernamee, error);
+ FROM pg_ident_file_mappings() a(mapping_number, file_name, line_number, map_name, sys_name, pg_usernamee, error);
pg_indexes| SELECT n.nspname AS schemaname,
c.relname AS tablename,
i.relname AS indexname,
--
2.33.1
--uqjaptwdgaq4xou6
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename="v2-0004-POC-Add-a-pg_hba_matches-function.patch"
view thread (33+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v2 3/4] Allow file inclusion in pg_hba and pg_ident files.
In-Reply-To: <no-message-id-1860165@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox