($INBOX_DIR/description missing)
help / color / mirror / Atom feedFrom: Julien Rouhaud <[email protected]>
Subject: [PATCH v5 2/3] Allow file inclusion in pg_hba and pg_ident files.
Date: Mon, 21 Feb 2022 15:45:26 +0800
Catversion is bumped.
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 | 229 +++++++++++++++++++------
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, 321 insertions(+), 85 deletions(-)
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 75fedfa07e..bd1c9a8d21 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -10496,12 +10496,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>
@@ -10637,6 +10656,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 556f473b41..1551b34c53 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,10 +118,16 @@ static const char *const UserAuthName[] =
};
+static void tokenize_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,
int elevel, char **err_msg);
+static FILE *open_inc_file(HbaIncludeKind kind, const char *inc_filename,
+ const char *outer_filename, int elevel,
+ char **err_msg, char **inc_fullname);
/*
@@ -355,36 +367,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);
@@ -425,11 +412,36 @@ tokenize_inc_file(List *tokens,
/*
* tokenize_auth_file
- * Tokenize the given file.
+ *
+ * Wrapper around tokenize_file_with_context, creating a decicated memory
+ * context.
+ *
+ * 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)
+{
+ MemoryContext linecxt;
+ linecxt = AllocSetContextCreate(CurrentMemoryContext,
+ "tokenize_auth_file",
+ ALLOCSET_SMALL_SIZES);
+
+ *tok_lines = NIL;
+
+ tokenize_file_with_context(linecxt, filename, file, tok_lines, elevel);
+
+ return linecxt;
+}
+
+/*
+ * Tokenize the given file.
*
* The output is a list of TokenizedAuthLine structs; see the struct definition
* in libpq/hba.h.
*
+ * 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
@@ -438,30 +450,22 @@ tokenize_inc_file(List *tokens,
* 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
- * this function (it's a child of caller's context).
*/
-MemoryContext
-tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
- int elevel)
+static void
+tokenize_file_with_context(MemoryContext linecxt, const char *filename,
+ FILE *file, List **tok_lines, int elevel)
{
- int line_number = 1;
StringInfoData buf;
- MemoryContext linecxt;
+ int line_number = 1;
MemoryContext oldcxt;
- linecxt = AllocSetContextCreate(CurrentMemoryContext,
- "tokenize_auth_file",
- ALLOCSET_SMALL_SIZES);
oldcxt = MemoryContextSwitchTo(linecxt);
initStringInfo(&buf);
- *tok_lines = NIL;
-
while (!feof(file) && !ferror(file))
{
+ TokenizedAuthLine *tok_line;
char *lineptr;
List *current_line = NIL;
char *err_msg = NULL;
@@ -522,29 +526,76 @@ tokenize_auth_file(const char *filename, FILE *file, List **tok_lines,
}
/*
- * Reached EOL; emit line to TokenizedAuthLine list unless it's boring
+ * Reached EOL; no need to emit line to TokenizedAuthLine list if it's
+ * boring.
*/
- if (current_line != NIL || err_msg != NULL)
+ 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)
{
- TokenizedAuthLine *tok_line;
+ AuthToken *first, *second;
+
+ first = linitial(linitial_node(List, current_line));
+ second = linitial(lsecond_node(List, current_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);
+ 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_file_with_context(linecxt, inc_fullname, inc_file,
+ tok_lines, elevel);
+
+ FreeFile(inc_file);
+ pfree(inc_fullname);
+
+ /*
+ * The line is fully 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);
-
- return linecxt;
}
-
/*
* Does user belong to role?
*
@@ -859,7 +910,7 @@ do { \
errmsg("authentication option \"%s\" is only valid for authentication methods %s", \
optname, _(validmethods)), \
errcontext("line %d of configuration file \"%s\"", \
- line_num, HbaFileName))); \
+ line_num, file_name))); \
*err_msg = psprintf("authentication option \"%s\" is only valid for authentication methods %s", \
optname, validmethods); \
return false; \
@@ -879,7 +930,7 @@ do { \
errmsg("authentication method \"%s\" requires argument \"%s\" to be set", \
authname, argname), \
errcontext("line %d of configuration file \"%s\"", \
- line_num, HbaFileName))); \
+ line_num, file_name))); \
*err_msg = psprintf("authentication method \"%s\" requires argument \"%s\" to be set", \
authname, argname); \
return NULL; \
@@ -901,7 +952,7 @@ do { \
ereport(elevel, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("missing entry in file \"%s\" at end of line %d", \
- IdentFileName, line_num))); \
+ tok_line->file_name, line_num))); \
*err_msg = psprintf("missing entry at end of line"); \
return NULL; \
} \
@@ -914,7 +965,7 @@ do { \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("multiple values in ident field"), \
errcontext("line %d of configuration file \"%s\"", \
- line_num, IdentFileName))); \
+ line_num, tok_line->file_name))); \
*err_msg = psprintf("multiple values in ident field"); \
return NULL; \
} \
@@ -937,6 +988,7 @@ HbaLine *
parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
{
int line_num = tok_line->line_num;
+ char *file_name = tok_line->file_name;
char **err_msg = &tok_line->err_msg;
char *str;
struct addrinfo *gai_result;
@@ -951,6 +1003,7 @@ parse_hba_line(TokenizedAuthLine *tok_line, int elevel)
HbaLine *parsedline;
parsedline = palloc0(sizeof(HbaLine));
+ parsedline->sourcefile = pstrdup(file_name);
parsedline->linenumber = line_num;
parsedline->rawline = pstrdup(tok_line->raw_line);
@@ -1677,6 +1730,7 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
int elevel, char **err_msg)
{
int line_num = hbaline->linenumber;
+ char *file_name = hbaline->sourcefile;
#ifdef USE_LDAP
hbaline->ldapscope = LDAP_SCOPE_SUBTREE;
@@ -2299,6 +2353,67 @@ load_hba(void)
return true;
}
+/*
+ * 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;
+}
/*
* Parse one tokenised line from the ident config file and store the result in
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 1970b4c497..33cf5fb954 100644
--- a/src/backend/utils/adt/hbafuncs.c
+++ b/src/backend/utils/adt/hbafuncs.c
@@ -26,9 +26,11 @@
static ArrayType *get_hba_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);
static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
+ int mapping_number, const char *filename,
int lineno, IdentLine *ident, const char *err_msg);
static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
@@ -157,7 +159,7 @@ get_hba_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
@@ -174,7 +176,8 @@ get_hba_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];
@@ -193,6 +196,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);
@@ -336,7 +346,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 */
@@ -359,6 +369,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;
@@ -393,8 +404,12 @@ fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
if (tok_line->err_msg == NULL)
hbaline = parse_hba_line(tok_line, DEBUG3);
- fill_hba_line(tuple_store, tupdesc, tok_line->line_num,
- hbaline, tok_line->err_msg);
+ /* No error, set a rule number */
+ if (tok_line->err_msg == NULL)
+ rule_number++;
+
+ fill_hba_line(tuple_store, tupdesc, rule_number, tok_line->file_name,
+ tok_line->line_num, hbaline, tok_line->err_msg);
}
/* Free tokenizer memory */
@@ -430,8 +445,8 @@ pg_hba_file_rules(PG_FUNCTION_ARGS)
PG_RETURN_NULL();
}
-/* Number of columns in pg_ident_file_mappings view */
-#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5
+/* Number of columns in pg_hba_file_mappings view */
+#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 7
/*
* fill_ident_line: build one row of pg_ident_file_mappings view, add it to
@@ -448,7 +463,8 @@ pg_hba_file_rules(PG_FUNCTION_ARGS)
*/
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];
@@ -461,6 +477,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);
@@ -473,7 +496,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 */
@@ -495,6 +518,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;
@@ -529,8 +553,13 @@ fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
if (tok_line->err_msg == NULL)
identline = parse_ident_line(tok_line, DEBUG3);
- fill_ident_line(tuple_store, tupdesc, tok_line->line_num, identline,
- tok_line->err_msg);
+ /* No error, set a rule number */
+ if (tok_line->err_msg == NULL)
+ mapping_number++;
+
+ 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 efd48741d8..04cab629f5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -6111,16 +6111,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_username,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_username,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 90036f7bcd..59f6faf9f8 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;
@@ -155,6 +156,7 @@ typedef struct AuthToken
typedef struct TokenizedAuthLine
{
List *fields; /* List of lists of AuthTokens */
+ 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 5a20e5a7e1..e2c6aa8ffb 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_username,
a.error
- FROM pg_ident_file_mappings() a(line_number, map_name, sys_name, pg_username, error);
+ FROM pg_ident_file_mappings() a(mapping_number, file_name, line_number, map_name, sys_name, pg_username, error);
pg_indexes| SELECT n.nspname AS schemaname,
c.relname AS tablename,
i.relname AS indexname,
--
2.33.1
--w64sw4oo7jkfb67p
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename="v5-0003-POC-Add-a-pg_hba_matches-function.patch"
view thread (32+ 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 v5 2/3] Allow file inclusion in pg_hba and pg_ident files.
In-Reply-To: <no-message-id-1860646@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