diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7b652460a1..a59d33074b 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -23660,6 +23660,23 @@ SELECT * FROM pg_ls_dir('.') WITH ORDINALITY AS t(ls,n); + + + + system_user + + system_user + name + + + Returns the authentication method and the identity (if any) that the + user presented during the authentication cycle, before they were + assigned a database role. It is represented as 'auth_method:identity' or + is NULL if the user has not actually been authenticated (for example if + the has been used). + + + diff --git a/src/backend/access/transam/parallel.c b/src/backend/access/transam/parallel.c index df0cd77558..3b49189bfd 100644 --- a/src/backend/access/transam/parallel.c +++ b/src/backend/access/transam/parallel.c @@ -76,6 +76,7 @@ #define PARALLEL_KEY_REINDEX_STATE UINT64CONST(0xFFFFFFFFFFFF000C) #define PARALLEL_KEY_RELMAPPER_STATE UINT64CONST(0xFFFFFFFFFFFF000D) #define PARALLEL_KEY_UNCOMMITTEDENUMS UINT64CONST(0xFFFFFFFFFFFF000E) +#define PARALLEL_KEY_SYSTEMUSER UINT64CONST(0xFFFFFFFFFFFF000F) /* Fixed-size parallel state. */ typedef struct FixedParallelState @@ -212,6 +213,7 @@ InitializeParallelDSM(ParallelContext *pcxt) Size reindexlen = 0; Size relmapperlen = 0; Size uncommittedenumslen = 0; + Size systemuserlen = 0; Size segsize = 0; int i; FixedParallelState *fps; @@ -272,8 +274,10 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_estimate_chunk(&pcxt->estimator, relmapperlen); uncommittedenumslen = EstimateUncommittedEnumsSpace(); shm_toc_estimate_chunk(&pcxt->estimator, uncommittedenumslen); + systemuserlen = EstimateSystemUserSpace(); + shm_toc_estimate_chunk(&pcxt->estimator, systemuserlen); /* If you add more chunks here, you probably need to add keys. */ - shm_toc_estimate_keys(&pcxt->estimator, 11); + shm_toc_estimate_keys(&pcxt->estimator, 12); /* Estimate space need for error queues. */ StaticAssertStmt(BUFFERALIGN(PARALLEL_ERROR_QUEUE_SIZE) == @@ -352,6 +356,7 @@ InitializeParallelDSM(ParallelContext *pcxt) char *session_dsm_handle_space; char *entrypointstate; char *uncommittedenumsspace; + char *systemuserspace; Size lnamelen; /* Serialize shared libraries we have loaded. */ @@ -422,6 +427,12 @@ InitializeParallelDSM(ParallelContext *pcxt) shm_toc_insert(pcxt->toc, PARALLEL_KEY_UNCOMMITTEDENUMS, uncommittedenumsspace); + /* Serialize our Systemuser. */ + systemuserspace = shm_toc_allocate(pcxt->toc, systemuserlen); + SerializeSystemUser(systemuserlen, systemuserspace); + shm_toc_insert(pcxt->toc, PARALLEL_KEY_SYSTEMUSER, + systemuserspace); + /* Allocate space for worker information. */ pcxt->worker = palloc0(sizeof(ParallelWorkerInfo) * pcxt->nworkers); @@ -1270,6 +1281,7 @@ ParallelWorkerMain(Datum main_arg) char *reindexspace; char *relmapperspace; char *uncommittedenumsspace; + char *systemuserspace; StringInfoData msgbuf; char *session_dsm_handle_space; Snapshot tsnapshot; @@ -1479,6 +1491,11 @@ ParallelWorkerMain(Datum main_arg) false); RestoreUncommittedEnums(uncommittedenumsspace); + /* Restore the SystemUser. */ + systemuserspace = shm_toc_lookup(toc, PARALLEL_KEY_SYSTEMUSER, + false); + RestoreSystemUser(systemuserspace); + /* Attach to the leader's serializable transaction, if SERIALIZABLE. */ AttachSerializableXact(fps->serializable_xact_handle); diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index eaec697bb3..431a94a3de 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -2543,6 +2543,11 @@ ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op) *op->resvalue = session_user(fcinfo); *op->resnull = fcinfo->isnull; break; + case SVFOP_SYSTEM_USER: + InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); + *op->resvalue = system_user(fcinfo); + *op->resnull = fcinfo->isnull; + break; case SVFOP_CURRENT_CATALOG: InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL); *op->resvalue = current_database(fcinfo); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 969c9c158f..29dcc77724 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -842,7 +842,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STORED STRICT_P STRING STRIP_P - SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P + SUBSCRIPTION SUBSTRING SUPPORT SYMMETRIC SYSID SYSTEM_P SYSTEM_USER TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIES TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM @@ -15457,6 +15457,10 @@ func_expr_common_subexpr: { $$ = makeSQLValueFunction(SVFOP_SESSION_USER, -1, @1); } + | SYSTEM_USER + { + $$ = makeSQLValueFunction(SVFOP_SYSTEM_USER, -1, @1); + } | USER { $$ = makeSQLValueFunction(SVFOP_USER, -1, @1); @@ -18173,6 +18177,7 @@ reserved_keyword: | SESSION_USER | SOME | SYMMETRIC + | SYSTEM_USER | TABLE | THEN | TO @@ -18578,6 +18583,7 @@ bare_label_keyword: | SYMMETRIC | SYSID | SYSTEM_P + | SYSTEM_USER | TABLE | TABLES | TABLESAMPLE diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 0dc2fc472e..8256cc177d 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -2299,6 +2299,7 @@ transformSQLValueFunction(ParseState *pstate, SQLValueFunction *svf) case SVFOP_CURRENT_USER: case SVFOP_USER: case SVFOP_SESSION_USER: + case SVFOP_SYSTEM_USER: case SVFOP_CURRENT_CATALOG: case SVFOP_CURRENT_SCHEMA: svf->type = NAMEOID; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 2a1d44b813..8ad50e31d2 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -1917,6 +1917,9 @@ FigureColnameInternal(Node *node, char **name) case SVFOP_SESSION_USER: *name = "session_user"; return 2; + case SVFOP_SYSTEM_USER: + *name = "system_user"; + return 2; case SVFOP_CURRENT_CATALOG: *name = "current_catalog"; return 2; diff --git a/src/backend/utils/adt/name.c b/src/backend/utils/adt/name.c index e8bba3670c..aa28b32cf0 100644 --- a/src/backend/utils/adt/name.c +++ b/src/backend/utils/adt/name.c @@ -257,7 +257,7 @@ namestrcmp(Name name, const char *str) /* - * SQL-functions CURRENT_USER, SESSION_USER + * SQL-functions CURRENT_USER, SESSION_USER, SYSTEM_USER */ Datum current_user(PG_FUNCTION_ARGS) @@ -271,6 +271,16 @@ session_user(PG_FUNCTION_ARGS) PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(GetUserNameFromId(GetSessionUserId(), false)))); } +Datum +system_user(PG_FUNCTION_ARGS) +{ + const char *sysuser = GetSystemUser(); + + if (sysuser) + PG_RETURN_DATUM(DirectFunctionCall1(namein, CStringGetDatum(sysuser))); + else + PG_RETURN_NULL(); +} /* * SQL-functions CURRENT_SCHEMA, CURRENT_SCHEMAS diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index c3937a60fd..ffdd818f1e 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9319,6 +9319,9 @@ get_rule_expr(Node *node, deparse_context *context, case SVFOP_SESSION_USER: appendStringInfoString(buf, "SESSION_USER"); break; + case SVFOP_SYSTEM_USER: + appendStringInfoString(buf, "SYSTEM_USER"); + break; case SVFOP_CURRENT_CATALOG: appendStringInfoString(buf, "CURRENT_CATALOG"); break; diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index b25bd0e583..75f1fe35e2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -473,6 +473,7 @@ static Oid AuthenticatedUserId = InvalidOid; static Oid SessionUserId = InvalidOid; static Oid OuterUserId = InvalidOid; static Oid CurrentUserId = InvalidOid; +static const char *SystemUser = NULL; /* We also have to remember the superuser state of some of these levels */ static bool AuthenticatedUserIsSuperuser = false; @@ -544,6 +545,16 @@ SetSessionUserId(Oid userid, bool is_superuser) CurrentUserId = userid; } +/* + * Return the system user representing the authenticated identity. + * It is defined in InitializeSystemUser() as auth_method:authn_id. + */ +const char * +GetSystemUser(void) +{ + return SystemUser; +} + /* * GetAuthenticatedUserId - get the authenticated user ID */ @@ -814,6 +825,32 @@ InitializeSessionUserIdStandalone(void) SetSessionUserId(BOOTSTRAP_SUPERUSERID, true); } +/* + * Initialize the system user during normal backend startup. + */ +void +InitializeSystemUser(const Port *port) +{ + /* call only once */ + Assert(SystemUser == NULL); + + if (port->authn_id) + { + /* Build sysuser as auth_method:authn_id */ + char *system_user; + Size authname_len = strlen(hba_authname(port->hba->auth_method)); + Size authn_id_len = strlen(port->authn_id); + + system_user = palloc0(authname_len + authn_id_len + 2); + strcat(system_user, hba_authname(port->hba->auth_method)); + strcat(system_user, ":"); + strcat(system_user, port->authn_id); + + /* Store SystemUser in long-lived storage */ + SystemUser = MemoryContextStrdup(TopMemoryContext, system_user); + pfree(system_user); + } +} /* * Change session auth ID while running @@ -932,6 +969,53 @@ GetUserNameFromId(Oid roleid, bool noerr) return result; } +/* + * Calculate the space needed to serialize SystemUser. + */ +Size +EstimateSystemUserSpace(void) +{ + Size size = 1; + const char *sysuser = GetSystemUser(); + + if (sysuser) + size = add_size(size, strlen(sysuser) + 1); + + return size; +} + +/* + * Serialize SystemUser for use by parallel workers. + */ +void +SerializeSystemUser(Size maxsize, char *start_address) +{ + const char *sysuser = GetSystemUser(); + + Assert(maxsize > 0); + + if (sysuser) + { + Size len; + len = strlcpy(start_address, sysuser, maxsize) + 1; + Assert(len <= maxsize); + } + else + start_address[0] = '\0'; +} + +/* + * Restore SystemUser from its serialized representation. + */ +void +RestoreSystemUser(char *sysuser) +{ + if (sysuser[0] == '\0') + SystemUser = NULL; + else + SystemUser = MemoryContextStrdup(TopMemoryContext, sysuser); +} + /*------------------------------------------------------------------------- * Interlock-file support diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index 6b9082604f..d9a879382e 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -835,6 +835,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, Assert(MyProcPort != NULL); PerformAuthentication(MyProcPort); InitializeSessionUserId(username, useroid); + InitializeSystemUser(MyProcPort); am_superuser = superuser(); } diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 87aa571a33..4406396055 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -1508,6 +1508,9 @@ { oid => '746', descr => 'session user name', proname => 'session_user', provolatile => 's', prorettype => 'name', proargtypes => '', prosrc => 'session_user' }, +{ oid => '786', descr => 'system user name', + proname => 'system_user', provolatile => 's', prorettype => 'name', + proargtypes => '', prosrc => 'system_user' }, { oid => '744', proname => 'array_eq', prorettype => 'bool', diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 0af130fbc5..8d761512fd 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -364,6 +364,10 @@ extern void InitializeSessionUserIdStandalone(void); extern void SetSessionAuthorization(Oid userid, bool is_superuser); extern Oid GetCurrentRoleId(void); extern void SetCurrentRoleId(Oid roleid, bool is_superuser); +/* kluge to avoid including libpq/libpq-be.h here */ +typedef struct Port MyPort; +extern void InitializeSystemUser(const MyPort *port); +extern const char* GetSystemUser(void); /* in utils/misc/superuser.c */ extern bool superuser(void); /* current user is superuser */ @@ -486,6 +490,10 @@ extern bool has_rolreplication(Oid roleid); typedef void (*shmem_request_hook_type) (void); extern PGDLLIMPORT shmem_request_hook_type shmem_request_hook; +extern Size EstimateSystemUserSpace(void); +extern void SerializeSystemUser(Size maxsize, char *start_address); +extern void RestoreSystemUser(char *sysuser); + /* in executor/nodeHash.c */ extern size_t get_hash_memory_limit(void); diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 51505eee85..c23b0f2507 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1186,6 +1186,7 @@ typedef enum SQLValueFunctionOp SVFOP_CURRENT_USER, SVFOP_USER, SVFOP_SESSION_USER, + SVFOP_SYSTEM_USER, SVFOP_CURRENT_CATALOG, SVFOP_CURRENT_SCHEMA } SQLValueFunctionOp; diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index ae35f03251..e309988b26 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -434,6 +434,7 @@ PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("system", SYSTEM_P, UNRESERVED_KEYWORD, BARE_LABEL) +PG_KEYWORD("system_user", SYSTEM_USER, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("table", TABLE, RESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("tables", TABLES, UNRESERVED_KEYWORD, BARE_LABEL) PG_KEYWORD("tablesample", TABLESAMPLE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL) diff --git a/src/test/authentication/t/001_password.pl b/src/test/authentication/t/001_password.pl index 3e3079c824..56620b1ffd 100644 --- a/src/test/authentication/t/001_password.pl +++ b/src/test/authentication/t/001_password.pl @@ -72,6 +72,13 @@ $node->safe_psql('postgres', $node->safe_psql('postgres', "SET password_encryption='md5'; CREATE ROLE md5_role LOGIN PASSWORD 'pass';" ); +# Set up a table for SYSTEM_USER parallel worker testing. +$node->safe_psql('postgres', + 'CREATE TABLE nulls (n) AS SELECT NULL FROM generate_series(1, 200000);' +); +$node->safe_psql('postgres', + 'GRANT SELECT ON nulls TO md5_role;' +); $ENV{"PGPASSWORD"} = 'pass'; # For "trust" method, all users should be able to connect. These users are not @@ -82,6 +89,24 @@ test_role($node, 'scram_role', 'trust', 0, test_role($node, 'md5_role', 'trust', 0, log_unlike => [qr/connection authenticated:/]); +# Test SYSTEM_USER is null when not authenticated. +my $res = + $node->safe_psql('postgres', "SELECT SYSTEM_USER IS NULL;"); +is($res, 't', "users with trust authentication have NULL SYSTEM_USER"); + +# Test SYSTEM_USER with parallel workers. +$res = $node->safe_psql( + 'postgres', ' + SET min_parallel_table_scan_size TO 0; + SET parallel_setup_cost TO 0; + SET parallel_tuple_cost TO 0; + SET max_parallel_workers_per_gather TO 2; + + SELECT bool_and(SYSTEM_USER IS NOT DISTINCT FROM n) FROM nulls; + ', + connstr => "user=md5_role"); +is($res, 't', "parallel workers return a null SYSTEM_USER when not authenticated"); + # For plain "password" method, all users should also be able to connect. reset_pg_hba($node, 'password'); test_role($node, 'scram_role', 'password', 0, diff --git a/src/test/kerberos/t/001_auth.pl b/src/test/kerberos/t/001_auth.pl index 62e0542639..9bf17985d5 100644 --- a/src/test/kerberos/t/001_auth.pl +++ b/src/test/kerberos/t/001_auth.pl @@ -4,8 +4,8 @@ # Sets up a KDC and then runs a variety of tests to make sure that the # GSSAPI/Kerberos authentication and encryption are working properly, # that the options in pg_hba.conf and pg_ident.conf are handled correctly, -# and that the server-side pg_stat_gssapi view reports what we expect to -# see for each test. +# that the server-side pg_stat_gssapi view reports what we expect to +# see for each test and that SYSTEM_USER returns what we expect to see. # # Since this requires setting up a full KDC, it doesn't make much sense # to have multiple test scripts (since they'd have to also create their @@ -307,6 +307,29 @@ test_query( 'gssencmode=require', 'sending 100K lines works'); +# Test that SYSTEM_USER works. +test_query( + $node, + 'test1', + 'SELECT SYSTEM_USER;', + qr/^gss:test1\@$realm$/s, + 'gssencmode=require', + 'testing system_user'); + +# Test that SYSTEM_USER works with parallel workers. +test_query( + $node, + 'test1', + "CREATE TEMP TABLE mytab (n) as SELECT NULL FROM generate_series(1, 200000);\n" + . "SET min_parallel_table_scan_size TO 0;\n" + . "SET parallel_setup_cost TO 0;\n" + . "SET parallel_tuple_cost TO 0;\n" + . "SET max_parallel_workers_per_gather TO 2;\n" + . "SELECT bool_and(SYSTEM_USER IS DISTINCT FROM n) FROM mytab;", + qr/^t$/s, + 'gssencmode=require', + 'testing system_user with parallel workers'); + unlink($node->data_dir . '/pg_hba.conf'); $node->append_conf('pg_hba.conf', qq{hostgssenc all all $hostaddr/32 gss map=mymap});