*** a/doc/src/sgml/ref/initdb.sgml --- b/doc/src/sgml/ref/initdb.sgml *************** *** 152,157 **** PostgreSQL documentation --- 152,170 ---- + + + + + This option enables the shared catalog tables security, by adding + row level security policies on all eligible shared catalog tables. + With this option, multi-tenancy in PostgreSQL is supported at + database level. + + + + + *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** *** 3436,3441 **** ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode) --- 3436,3450 ---- { AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab); + /* + * ALTER table on sytem catalog tables is possible only when user specifies + * CATALOG SECURITY on system catalog tables. To avoid an error in the + * AlterTableCreateToastTable function for system catalog tables, the system + * catalog tables are ignored for the toast table creation. + */ + if (!IsUnderPostmaster && IsSharedRelation(tab->relid)) + continue; + if (tab->relkind == RELKIND_RELATION || tab->relkind == RELKIND_MATVIEW) AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode); *** a/src/backend/utils/misc/rls.c --- b/src/backend/utils/misc/rls.c *************** *** 58,67 **** check_enable_rls(Oid relid, Oid checkAsUser, bool noError) bool relforcerowsecurity; Oid user_id = checkAsUser ? checkAsUser : GetUserId(); - /* Nothing to do for built-in relations */ - if (relid < FirstNormalObjectId) - return RLS_NONE; - tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid)); if (!HeapTupleIsValid(tuple)) return RLS_NONE; --- 58,63 ---- *** a/src/bin/initdb/initdb.c --- b/src/bin/initdb/initdb.c *************** *** 132,137 **** static bool do_sync = true; --- 132,138 ---- static bool sync_only = false; static bool show_setting = false; static bool data_checksums = false; + static bool shared_catalog_security = false; static char *xlog_dir = ""; *************** *** 186,191 **** static char *authwarning = NULL; --- 187,193 ---- */ static const char *boot_options = "-F"; static const char *backend_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true"; + static const char *catalog_security_options = "--single -F -O -c search_path=pg_catalog -c exit_on_error=true -c allow_system_table_mods=true"; static const char *subdirs[] = { "global", *************** *** 255,260 **** static void setup_dictionary(void); --- 257,263 ---- static void setup_privileges(void); static void set_info_version(void); static void setup_schema(void); + static void setup_shared_catalog_security(void); static void load_plpgsql(void); static void vacuum_db(void); static void make_template0(void); *************** *** 2252,2257 **** setup_schema(void) --- 2255,2356 ---- } /* + * setup shared catalog security by defining policies + */ + static void + setup_shared_catalog_security(void) + { + PG_CMD_DECL; + const char **line; + static const char *pg_shared_catalog_security_setup[] = { + /* AuthMemRelationId */ + "create policy pg_auth_members_read_own_data on pg_auth_members for select using" + " (pg_has_role(roleid, 'any') AND pg_has_role(member, 'any'));\n", + + "alter table pg_auth_members enable row level security;\n", + + /* AuthIdRelationId */ + "create policy pg_authid_read_own_data on pg_authid for select using" + " (pg_has_role(oid, 'any'));\n", + + "alter table pg_authid enable row level security;\n", + + /* DatabaseRelationId */ + "create policy pg_database_read_own_data on pg_database for select using" + " ((oid < 16384) OR has_database_privilege(oid,'any'));\n", + + "alter table pg_database enable row level security;\n", + + /* DbRoleSettingRelationId */ + "create policy pg_db_role_setting_read_own_data on pg_db_role_setting for select using" + " (pg_has_role(setrole, 'any') AND has_database_privilege(setdatabase,'any'));\n", + + "alter table pg_database enable row level security;\n", + + /* PLTemplateRelationId */ + /* + * Currently there is no policy needed for this table, so + * leave it as it is. + */ + + /* ReplicationOriginRelationId */ + /* + * Currently there is no policy needed for this table, so + * leave it as it is. + */ + + /* SharedDependRelationId */ + "create policy pg_shdepend_read_own_data on pg_shdepend for select using" + " ((classid = 1262 AND has_database_privilege(objid, 'any'))" + " OR (classid = 1260 AND pg_has_role(objid, 'any'))" + " OR (classid = 1213 AND has_tablespace_privilege(objid, 'any')));\n", + + "alter table pg_shdepend enable row level security;\n", + + /* SharedDescriptionRelationId */ + "create policy pg_shdescription_read_own_data on pg_shdescription for select using" + " ((classoid = 1262 AND has_database_privilege(objoid, 'any'))" + " OR (classoid = 1260 AND pg_has_role(objoid, 'any'))" + " OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n", + + "alter table pg_shdescription enable row level security;\n", + + /* SharedSecLabelRelationId */ + "create policy pg_shseclabel_read_own_data on pg_shseclabel for select using" + " ((classoid = 1262 AND has_database_privilege(objoid, 'any'))" + " OR (classoid = 1260 AND pg_has_role(objoid, 'any'))" + " OR (classoid = 1213 AND has_tablespace_privilege(objoid, 'any')));\n", + + "alter table pg_shseclabel enable row level security;\n", + + /* TableSpaceRelationId */ + "create policy pg_tablespace_read_own_data on pg_tablespace for select using" + " ((oid < 16384) OR has_tablespace_privilege(oid, 'any'));\n", + + "alter table pg_tablespace enable row level security;\n", + + NULL + }; + + fputs(_("creating shared catalog security policy ... "), stdout); + fflush(stdout); + + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, catalog_security_options, + DEVNULL); + + PG_CMD_OPEN; + + for (line = pg_shared_catalog_security_setup; *line != NULL; line++) + PG_CMD_PUTS(*line); + + PG_CMD_CLOSE; + + check_ok(); + } + + /* * load PL/pgsql server-side language */ static void *************** *** 2768,2773 **** usage(const char *progname) --- 2867,2874 ---- printf(_("\nLess commonly used options:\n")); printf(_(" -d, --debug generate lots of debugging output\n")); printf(_(" -k, --data-checksums use data page checksums\n")); + printf(_(" -C, --shared-catalog-security\n" + " use shared catalog security\n")); printf(_(" -L DIRECTORY where to find the input files\n")); printf(_(" -n, --noclean do not clean up after errors\n")); printf(_(" -N, --nosync do not wait for changes to be written safely to disk\n")); *************** *** 3365,3370 **** initialize_data_directory(void) --- 3466,3474 ---- setup_schema(); + if (shared_catalog_security) + setup_shared_catalog_security(); + load_plpgsql(); vacuum_db(); *************** *** 3405,3410 **** main(int argc, char *argv[]) --- 3509,3515 ---- {"sync-only", no_argument, NULL, 'S'}, {"xlogdir", required_argument, NULL, 'X'}, {"data-checksums", no_argument, NULL, 'k'}, + {"shared-catalog-security", no_argument, NULL, 'C' }, {NULL, 0, NULL, 0} }; *************** *** 3445,3451 **** main(int argc, char *argv[]) /* process command-line options */ ! while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1) { switch (c) { --- 3550,3556 ---- /* process command-line options */ ! while ((c = getopt_long(argc, argv, "dD:E:kCL:nNU:WA:sST:X:", long_options, &option_index)) != -1) { switch (c) { *************** *** 3497,3502 **** main(int argc, char *argv[]) --- 3602,3610 ---- case 'k': data_checksums = true; break; + case 'C': + shared_catalog_security = true; + break; case 'L': share_path = pg_strdup(optarg); break;