<entry>summary of client authentication configuration file contents</entry>
</row>
+ <row>
+ <entry><link linkend="view-pg-ident-file-mappings"><structname>pg_ident_file_mappings</structname></link></entry>
+ <entry>summary of client user name mapping configuration file contents</entry>
+ </row>
+
<row>
<entry><link linkend="view-pg-indexes"><structname>pg_indexes</structname></link></entry>
<entry>indexes</entry>
</para>
</sect1>
+ <sect1 id="view-pg-ident-file-mappings">
+ <title><structname>pg_ident_file_mappings</structname></title>
+
+ <indexterm zone="view-pg-ident-file-mappings">
+ <primary>pg_ident_file_mappings</primary>
+ </indexterm>
+
+ <para>
+ The view <structname>pg_ident_file_mappings</structname> provides a summary
+ of the contents of the client user name mapping configuration file,
+ <link linkend="auth-username-maps"><filename>pg_ident.conf</filename></link>.
+ A row appears in this view for each non-empty, non-comment line in the file,
+ with annotations indicating whether the rule could be applied successfully.
+ </para>
+
+ <para>
+ This view can be helpful for checking whether planned changes in the
+ authentication configuration file will work, or for diagnosing a previous
+ failure. Note that this view reports on the <emphasis>current</emphasis>
+ contents of the file, not on what was last loaded by the server.
+ </para>
+
+ <para>
+ By default, the <structname>pg_ident_file_mappings</structname> view can be
+ read only by superusers.
+ </para>
+
+ <table>
+ <title><structname>pg_ident_file_mappings</structname> Columns</title> <tgroup
+ cols="1">
+ <thead>
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ Column Type
+ </para>
+ <para>
+ Description
+ </para></entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <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_ident.conf</filename>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>map_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Name of the map
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>sys_name</structfield> <type>text</type>
+ </para>
+ <para>
+ Detected user name of the client
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>pg_username</structfield> <type>text</type>
+ </para>
+ <para>
+ Requested PostgreSQL user name
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="catalog_table_entry"><para role="column_definition">
+ <structfield>error</structfield> <type>text</type>
+ </para>
+ <para>
+ If not <literal>NULL</literal>, an error message indicating why this
+ line could not be processed
+ </para></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Usually, a row reflecting an incorrect entry will have values for only
+ the <structfield>line_number</structfield> and <structfield>error</structfield> fields.
+ </para>
+
+ <para>
+ See <xref linkend="client-authentication"/> for more information about
+ client authentication configuration.
+ </para>
+ </sect1>
+
<sect1 id="view-pg-indexes">
<title><structname>pg_indexes</structname></title>
-HUP</literal>) to make it re-read the file.
</para>
+ <para>
+ The system view
+ <link linkend="view-pg-ident-file-mappings"><structname>pg_ident_file_mappings</structname></link>
+ can be helpful for pre-testing changes to the
+ <filename>pg_ident.conf</filename> file, or for diagnosing problems if
+ loading of the file did not have the desired effects. Rows in the view with
+ non-null <structfield>error</structfield> fields indicate problems in the
+ corresponding lines of the file.
+ </para>
+
<para>
A <filename>pg_ident.conf</filename> file that could be used in
conjunction with the <filename>pg_hba.conf</filename> file in <xref
sending a <systemitem>SIGHUP</systemitem> signal to the postmaster
process, which in turn sends <systemitem>SIGHUP</systemitem> to each
of its children.) You can use the
- <link linkend="view-pg-file-settings"><structname>pg_file_settings</structname></link> and
- <link linkend="view-pg-hba-file-rules"><structname>pg_hba_file_rules</structname></link> views
+ <link linkend="view-pg-file-settings"><structname>pg_file_settings</structname></link>,
+ <link linkend="view-pg-hba-file-rules"><structname>pg_hba_file_rules</structname></link> and
+ <link linkend="view-pg-hba-file-rules"><structname>pg_ident_file_mappings</structname></link> views
to check the configuration files for possible errors, before reloading.
</para></entry>
</row>
REVOKE ALL ON pg_hba_file_rules FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION pg_hba_file_rules() FROM PUBLIC;
+CREATE VIEW pg_ident_file_mappings AS
+ SELECT * FROM pg_ident_file_mappings() AS A;
+
+REVOKE ALL ON pg_ident_file_mappings FROM PUBLIC;
+REVOKE EXECUTE ON FUNCTION pg_ident_file_mappings() FROM PUBLIC;
+
CREATE VIEW pg_timezone_abbrevs AS
SELECT * FROM pg_timezone_abbrevs();
} while (0)
/*
- * Macros for handling pg_ident problems.
- * Much as above, but currently the message level is hardwired as LOG
- * and there is no provision for an err_msg string.
+ * Macros for handling pg_ident problems, similar as above.
*
* IDENT_FIELD_ABSENT:
- * Log a message and exit the function if the given ident field ListCell is
- * not populated.
+ * Reports when the given ident field ListCell is not populated.
*
* IDENT_MULTI_VALUE:
- * Log a message and exit the function if the given ident token List has more
- * than one element.
+ * Reports when the given ident token List has more than one element.
*/
#define IDENT_FIELD_ABSENT(field) \
do { \
if (!field) { \
- ereport(LOG, \
+ ereport(elevel, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("missing entry in file \"%s\" at end of line %d", \
IdentFileName, line_num))); \
+ *err_msg = psprintf("missing entry at end of line"); \
return NULL; \
} \
} while (0)
#define IDENT_MULTI_VALUE(tokens) \
do { \
if (tokens->length > 1) { \
- ereport(LOG, \
+ ereport(elevel, \
(errcode(ERRCODE_CONFIG_FILE_ERROR), \
errmsg("multiple values in ident field"), \
errcontext("line %d of configuration file \"%s\"", \
line_num, IdentFileName))); \
+ *err_msg = psprintf("multiple values in ident field"); \
return NULL; \
} \
} while (0)
* Parse one tokenised line from the ident config file and store the result in
* an IdentLine structure.
*
- * If parsing fails, log a message and return NULL.
+ * If parsing fails, log a message at ereport level elevel, store an error
+ * string in tok_line->err_msg and return NULL.
*
* If ident_user is a regular expression (ie. begins with a slash), it is
* compiled and stored in IdentLine structure.
* to have set a memory context that will be reset if this function returns
* NULL.
*/
-static IdentLine *
-parse_ident_line(TokenizedAuthLine *tok_line)
+IdentLine *
+parse_ident_line(TokenizedAuthLine *tok_line, int elevel)
{
int line_num = tok_line->line_num;
+ char **err_msg = &tok_line->err_msg;
ListCell *field;
List *tokens;
AuthToken *token;
char errstr[100];
pg_regerror(r, &parsedline->re, errstr, sizeof(errstr));
- ereport(LOG,
+ ereport(elevel,
(errcode(ERRCODE_INVALID_REGULAR_EXPRESSION),
errmsg("invalid regular expression \"%s\": %s",
parsedline->ident_user + 1, errstr)));
+ *err_msg = psprintf("invalid regular expression \"%s\": %s",
+ parsedline->ident_user + 1, errstr);
+
pfree(wstr);
return NULL;
}
continue;
}
- if ((newline = parse_ident_line(tok_line)) == NULL)
+ if ((newline = parse_ident_line(tok_line, LOG)) == NULL)
{
/* Parse error; remember there's trouble */
ok = false;
static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
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 lineno, IdentLine *ident, const char *err_msg);
+static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc);
/*
PG_RETURN_NULL();
}
+
+/* Number of columns in pg_ident_file_mappings view */
+#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5
+
+/*
+ * fill_ident_line: build one row of pg_ident_file_mappings view, add it to
+ * tuplestore
+ *
+ * tuple_store: where to store data
+ * tupdesc: tuple descriptor for the view
+ * lineno: pg_ident.conf line number (must always be valid)
+ * ident: parsed line data (can be NULL, in which case err_msg should be set)
+ * err_msg: error message (NULL if none)
+ *
+ * Note: leaks memory, but we don't care since this is run in a short-lived
+ * memory context.
+ */
+static void
+fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
+ int lineno, IdentLine *ident, const char *err_msg)
+{
+ Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
+ bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS];
+ HeapTuple tuple;
+ int index;
+
+ Assert(tupdesc->natts == NUM_PG_IDENT_FILE_MAPPINGS_ATTS);
+
+ memset(values, 0, sizeof(values));
+ memset(nulls, 0, sizeof(nulls));
+ index = 0;
+
+ /* line_number */
+ values[index++] = Int32GetDatum(lineno);
+
+ if (ident != NULL)
+ {
+ values[index++] = CStringGetTextDatum(ident->usermap);
+ values[index++] = CStringGetTextDatum(ident->ident_user);
+ values[index++] = CStringGetTextDatum(ident->pg_role);
+ }
+ else
+ {
+ /* no parsing result, so set relevant fields to nulls */
+ memset(&nulls[1], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 2) * sizeof(bool));
+ }
+
+ /* error */
+ if (err_msg)
+ values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = CStringGetTextDatum(err_msg);
+ else
+ nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = true;
+
+ tuple = heap_form_tuple(tupdesc, values, nulls);
+ tuplestore_puttuple(tuple_store, tuple);
+}
+
+/*
+ * Read the pg_ident.conf file and fill the tuplestore with view records.
+ */
+static void
+fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc)
+{
+ FILE *file;
+ List *ident_lines = NIL;
+ ListCell *line;
+ MemoryContext linecxt;
+ MemoryContext identcxt;
+ MemoryContext oldcxt;
+
+ /*
+ * In the unlikely event that we can't open pg_ident.conf, we throw an
+ * error, rather than trying to report it via some sort of view entry.
+ * (Most other error conditions should result in a message in a view
+ * entry.)
+ */
+ file = AllocateFile(IdentFileName, "r");
+ if (file == NULL)
+ ereport(ERROR,
+ (errcode_for_file_access(),
+ errmsg("could not open usermap file \"%s\": %m",
+ IdentFileName)));
+
+ linecxt = tokenize_auth_file(HbaFileName, file, &ident_lines, DEBUG3);
+ FreeFile(file);
+
+ /* Now parse all the lines */
+ identcxt = AllocSetContextCreate(CurrentMemoryContext,
+ "ident parser context",
+ ALLOCSET_SMALL_SIZES);
+ oldcxt = MemoryContextSwitchTo(identcxt);
+ foreach(line, ident_lines)
+ {
+ TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line);
+ IdentLine *identline = NULL;
+
+ /* don't parse lines that already have errors */
+ 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);
+ }
+
+ /* Free tokenizer memory */
+ MemoryContextDelete(linecxt);
+ /* Free parse_ident_line memory */
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextDelete(identcxt);
+}
+
+/*
+ * SQL-accessible SRF to return all the entries in the pg_ident.conf file.
+ */
+Datum
+pg_ident_file_mappings(PG_FUNCTION_ARGS)
+{
+ ReturnSetInfo *rsi;
+
+ /*
+ * Build tuplestore to hold the result rows. We must use the Materialize
+ * mode to be safe against HBA file changes while the cursor is open. It's
+ * also more efficient than having to look up our current position in the
+ * parsed list every time.
+ */
+ SetSingleFuncCall(fcinfo, 0);
+
+ /* Fill the tuplestore */
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+ fill_ident_view(rsi->setResult, rsi->setDesc);
+
+ PG_RETURN_NULL();
+}
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202203272
+#define CATALOG_VERSION_NO 202203291
#endif
proargmodes => '{o,o,o,o,o,o,o,o,o}',
proargnames => '{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}',
+ prosrc => 'pg_ident_file_mappings' },
{ oid => '1371', descr => 'view system lock information',
proname => 'pg_lock_status', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
const char *pg_role, const char *auth_user,
bool case_sensitive);
extern HbaLine *parse_hba_line(TokenizedAuthLine *tok_line, int elevel);
+extern IdentLine *parse_ident_line(TokenizedAuthLine *tok_line, int elevel);
extern bool pg_isblank(const char c);
extern MemoryContext tokenize_auth_file(const char *filename, FILE *file,
List **tok_lines, int elevel);
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,
+ 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);
pg_indexes| SELECT n.nspname AS schemaname,
c.relname AS tablename,
i.relname AS indexname,
t | t
(1 row)
+-- There may be no rules, and there should be no errors.
+select count(*) >= 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
+ from pg_ident_file_mappings;
+ ok | no_err
+----+--------
+ t | t
+(1 row)
+
-- There will surely be at least one active lock
select count(*) > 0 as ok from pg_locks;
ok
select count(*) > 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
from pg_hba_file_rules;
+-- There may be no rules, and there should be no errors.
+select count(*) >= 0 as ok, count(*) FILTER (WHERE error IS NOT NULL) = 0 AS no_err
+ from pg_ident_file_mappings;
+
-- There will surely be at least one active lock
select count(*) > 0 as ok from pg_locks;