Alvaro Herrera wrote: > The fact that the ROLE_ATTR_* definitions are in pg_authid.h means that > there are now a lot of files that need to include that one. I think the > includes relative to ACLs and roles is rather messy now, and this patch > makes it a bit worse. > > I think we should create a new header file (maybe acltypes.h or > acldefs.h), which only contains the AclMode and RoleAttr typedefs and > their associated defines; that one would be included from parsenodes.h, > acl.h and pg_authid.h. Everything else would be in acl.h. So code that > currently checks permissions using only acl.h do not need any extra > includes.
I propose this patch on top of Adam's v5. Also included is a full patch against master. Unrelated: when changing from unified to context format, I saw filterdiff fail to produce a complete patch, skipping some hunks in its output. My first impression was that I had dropped some hunks in git, so I wasted some time comparing v5 and v6 by hand before remembering that Michael Paquier had mentioned this problem previously. -- Álvaro Herrera http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 8475985..3181a79 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -22,7 +22,6 @@ #include "access/xlog_internal.h" #include "access/xlogutils.h" #include "catalog/catalog.h" -#include "catalog/pg_authid.h" #include "catalog/pg_type.h" #include "funcapi.h" #include "miscadmin.h" diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index a403c64..a6de2ff 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h # indexing.h had better be last, and toasting.h just before it. POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ - pg_proc.h pg_type.h pg_attribute.h pg_class.h \ + acldefs.h pg_proc.h pg_type.h pg_attribute.h pg_class.h \ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index 4663429..21d282c 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -5038,18 +5038,18 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid) * roleid - the oid of the role to check. * attribute - the attribute to check. * - * Note: Use this function for role attribute permission checking as it accounts - * for superuser status. It will always return true for roles with superuser - * privileges unless the attribute being checked is CATUPDATE (superusers are not - * allowed to bypass CATUPDATE permissions). + * Note: Use this function for role attribute permission checking as it + * accounts for superuser status. It will always return true for roles with + * superuser privileges unless the attribute being checked is CATUPDATE + * (superusers are not allowed to bypass CATUPDATE permissions). * - * Note: roles do not have owners per se; instead we use this test in - * places where an ownership-like permissions test is needed for a role. - * Be sure to apply it to the role trying to do the operation, not the - * role being operated on! Also note that this generally should not be - * considered enough privilege if the target role is a superuser. - * (We don't handle that consideration here because we want to give a - * separate error message for such cases, so the caller has to deal with it.) + * Note: roles do not have owners per se; instead we use this test in places + * where an ownership-like permissions test is needed for a role. Be sure to + * apply it to the role trying to do the operation, not the role being operated + * on! Also note that this generally should not be considered enough privilege + * if the target role is a superuser. (We don't handle that consideration here + * because we want to give a separate error message for such cases, so the + * caller has to deal with it.) */ bool has_role_attribute(Oid roleid, RoleAttr attribute) diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index 2929b66..415ac17 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -91,7 +91,7 @@ my $BOOTSTRAP_SUPERUSERID = my $PG_CATALOG_NAMESPACE = find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE'); my $ROLE_ATTR_ALL = - find_defined_symbol('pg_authid.h', 'ROLE_ATTR_ALL'); + find_defined_symbol('acldefs.h', 'ROLE_ATTR_ALL'); # Read all the input header files into internal data structures my $catalogs = Catalog::Catalogs(@input_files); diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 5bde610..564f77a 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -73,14 +73,7 @@ CreateRole(CreateRoleStmt *stmt) char *password = NULL; /* user password */ bool encrypt_password = Password_encryption; /* encrypt password? */ char encrypted_password[MD5_PASSWD_LEN + 1]; - bool issuper = false; /* Make the user a superuser? */ - bool inherit = true; /* Auto inherit privileges? */ - bool createrole = false; /* Can this user create roles? */ - bool createdb = false; /* Can the user create databases? */ - bool canlogin = false; /* Can this user login? */ - bool isreplication = false; /* Is this a replication role? */ - bool bypassrls = false; /* Is this a row security enabled role? */ - RoleAttr attributes = ROLE_ATTR_NONE; /* role attributes, initialized to none. */ + RoleAttr attributes; int connlimit = -1; /* maximum connections allowed */ List *addroleto = NIL; /* roles to make this a member of */ List *rolemembers = NIL; /* roles to be members of this role */ @@ -102,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt) DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; - /* The defaults can vary depending on the original statement type */ + /* + * Every role has INHERIT by default, and CANLOGIN depends on the statement + * type. + */ + attributes = ROLE_ATTR_INHERIT; switch (stmt->stmt_type) { case ROLESTMT_ROLE: break; case ROLESTMT_USER: - canlogin = true; + attributes |= ROLE_ATTR_CANLOGIN; /* may eventually want inherit to default to false here */ break; case ROLESTMT_GROUP: @@ -243,21 +240,74 @@ CreateRole(CreateRoleStmt *stmt) if (dpassword && dpassword->arg) password = strVal(dpassword->arg); - /* Role Attributes */ + /* Set up role attributes and check permissions to set each of them */ if (dissuper) - issuper = intVal(dissuper->arg) != 0; + { + if (intVal(dissuper->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create superusers"))); + attributes |= ROLE_ATTR_SUPERUSER; + } + else + attributes &= ~ROLE_ATTR_SUPERUSER; + } if (dinherit) - inherit = intVal(dinherit->arg) != 0; + { + if (intVal(dinherit->arg) != 0) + attributes |= ROLE_ATTR_INHERIT; + else + attributes &= ~ROLE_ATTR_INHERIT; + } if (dcreaterole) - createrole = intVal(dcreaterole->arg) != 0; + { + if (intVal(dcreaterole->arg) != 0) + attributes |= ROLE_ATTR_CREATEROLE; + else + attributes &= ~ROLE_ATTR_CREATEROLE; + } if (dcreatedb) - createdb = intVal(dcreatedb->arg) != 0; + { + if (intVal(dcreatedb->arg) != 0) + attributes |= ROLE_ATTR_CREATEDB; + else + attributes &= ~ROLE_ATTR_CREATEDB; + } if (dcanlogin) - canlogin = intVal(dcanlogin->arg) != 0; + { + if (intVal(dcanlogin->arg) != 0) + attributes |= ROLE_ATTR_CANLOGIN; + else + attributes &= ~ROLE_ATTR_CANLOGIN; + } if (disreplication) - isreplication = intVal(disreplication->arg) != 0; + { + if (intVal(disreplication->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create replication users"))); + attributes |= ROLE_ATTR_REPLICATION; + } + else + attributes &= ~ROLE_ATTR_REPLICATION; + } if (dbypassRLS) - bypassrls = intVal(dbypassRLS->arg) != 0; + { + if (intVal(dbypassRLS->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to change bypassrls attribute"))); + attributes |= ROLE_ATTR_BYPASSRLS; + } + else + attributes &= ~ROLE_ATTR_BYPASSRLS; + } if (dconnlimit) { @@ -276,35 +326,11 @@ CreateRole(CreateRoleStmt *stmt) if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); - /* Check some permissions first */ - if (issuper) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create superusers"))); - } - else if (isreplication) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create replication users"))); - } - else if (bypassrls) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to change bypassrls attribute."))); - } - else - { - if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to create role"))); - } + /* Check permissions */ + if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create role"))); if (strcmp(stmt->role, "public") == 0 || strcmp(stmt->role, "none") == 0) @@ -351,22 +377,6 @@ CreateRole(CreateRoleStmt *stmt) validUntil_datum, validUntil_null); - /* Set all role attributes */ - if (issuper) - attributes |= ROLE_ATTR_SUPERUSER; - if (inherit) - attributes |= ROLE_ATTR_INHERIT; - if (createrole) - attributes |= ROLE_ATTR_CREATEROLE; - if (createdb) - attributes |= ROLE_ATTR_CREATEDB; - if (canlogin) - attributes |= ROLE_ATTR_CANLOGIN; - if (isreplication) - attributes |= ROLE_ATTR_REPLICATION; - if (bypassrls) - attributes |= ROLE_ATTR_BYPASSRLS; - /* * Build a tuple to insert */ @@ -663,8 +673,8 @@ AlterRole(AlterRoleStmt *stmt) roleid = HeapTupleGetOid(tuple); /* - * To mess with a superuser you gotta be superuser; else you need - * createrole, or just want to change your own password + * To mess with a superuser or a replication user you gotta be superuser; + * else you need createrole, or just want to change your own password */ attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr; @@ -751,50 +761,64 @@ AlterRole(AlterRoleStmt *stmt) */ if (issuper >= 0) { - attributes = (issuper > 0) ? attributes | (ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE) - : attributes & ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE); + if (issuper > 0) + attributes |= ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE; + else + attributes &= ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE); new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (inherit >= 0) { - attributes = (inherit > 0) ? attributes | ROLE_ATTR_INHERIT - : attributes & ~(ROLE_ATTR_INHERIT); + if (inherit > 0) + attributes |= ROLE_ATTR_INHERIT; + else + attributes &= ~ROLE_ATTR_INHERIT; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (createrole >= 0) { - attributes = (createrole > 0) ? attributes | ROLE_ATTR_CREATEROLE - : attributes & ~(ROLE_ATTR_CREATEROLE); + if (createrole > 0) + attributes |= ROLE_ATTR_CREATEROLE; + else + attributes &= ~ROLE_ATTR_CREATEROLE; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (createdb >= 0) { - attributes = (createdb > 0) ? attributes | ROLE_ATTR_CREATEDB - : attributes & ~(ROLE_ATTR_CREATEDB); + if (createdb > 0) + attributes |= ROLE_ATTR_CREATEDB; + else + attributes &= ~ROLE_ATTR_CREATEDB; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (canlogin >= 0) { - attributes = (canlogin > 0) ? attributes | ROLE_ATTR_CANLOGIN - : attributes & ~(ROLE_ATTR_CANLOGIN); + if (canlogin > 0) + attributes |= ROLE_ATTR_CANLOGIN; + else + attributes &= ~ROLE_ATTR_CANLOGIN; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (isreplication >= 0) { - attributes = (isreplication > 0) ? attributes | ROLE_ATTR_REPLICATION - : attributes & ~(ROLE_ATTR_REPLICATION); + if (isreplication > 0) + attributes |= ROLE_ATTR_REPLICATION; + else + attributes &= ~ROLE_ATTR_REPLICATION; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (bypassrls >= 0) { - attributes = (bypassrls > 0) ? attributes | ROLE_ATTR_BYPASSRLS - : attributes & ~(ROLE_ATTR_BYPASSRLS); + if (bypassrls > 0) + attributes |= ROLE_ATTR_BYPASSRLS; + else + attributes &= ~ROLE_ATTR_BYPASSRLS; new_record_repl[Anum_pg_authid_rolattr - 1] = true; } diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c index 5f1126e..1a38f56 100644 --- a/src/backend/replication/logical/logicalfuncs.c +++ b/src/backend/replication/logical/logicalfuncs.c @@ -17,19 +17,13 @@ #include <unistd.h> +#include "access/xlog_internal.h" +#include "catalog/pg_type.h" #include "fmgr.h" #include "funcapi.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" - -#include "access/xlog_internal.h" - -#include "catalog/pg_authid.h" -#include "catalog/pg_type.h" - #include "nodes/makefuncs.h" - -#include "mb/pg_wchar.h" - #include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" @@ -38,11 +32,9 @@ #include "utils/pg_lsn.h" #include "utils/resowner.h" #include "utils/lsyscache.h" - #include "replication/decode.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" - #include "storage/fd.h" /* private date for writing out data */ diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index bc6a23a..c113a0b 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -17,7 +17,6 @@ #include "miscadmin.h" #include "access/htup_details.h" -#include "catalog/pg_authid.h" #include "replication/slot.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 58633cc..f41ad34 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -36,7 +36,6 @@ #include "access/heapam.h" #include "access/htup_details.h" #include "access/sysattr.h" -#include "catalog/pg_authid.h" #include "catalog/pg_class.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_policy.h" diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 93017b2..4c03955 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -4723,9 +4723,7 @@ pg_all_role_attributes(PG_FUNCTION_ARGS) int i = 0; /* - * If no attributes are assigned, then there is no need to go through the - * individual checks for which are assigned. Therefore, we can short circuit - * and return an empty array. + * Short-circuit the case for no attributes assigned. */ if (attributes == ROLE_ATTR_NONE) PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID)); @@ -4734,21 +4732,21 @@ pg_all_role_attributes(PG_FUNCTION_ARGS) /* Determine which attributes are assigned. */ if (attributes & ROLE_ATTR_SUPERUSER) - temp_array[i++] = CStringGetTextDatum("Superuser"); + temp_array[i++] = CStringGetTextDatum(_("Superuser")); if (attributes & ROLE_ATTR_INHERIT) - temp_array[i++] = CStringGetTextDatum("Inherit"); + temp_array[i++] = CStringGetTextDatum(_("Inherit")); if (attributes & ROLE_ATTR_CREATEROLE) - temp_array[i++] = CStringGetTextDatum("Create Role"); + temp_array[i++] = CStringGetTextDatum(_("Create Role")); if (attributes & ROLE_ATTR_CREATEDB) - temp_array[i++] = CStringGetTextDatum("Create DB"); + temp_array[i++] = CStringGetTextDatum(_("Create DB")); if (attributes & ROLE_ATTR_CATUPDATE) - temp_array[i++] = CStringGetTextDatum("Catalog Update"); + temp_array[i++] = CStringGetTextDatum(_("Catalog Update")); if (attributes & ROLE_ATTR_CANLOGIN) - temp_array[i++] = CStringGetTextDatum("Login"); + temp_array[i++] = CStringGetTextDatum(_("Login")); if (attributes & ROLE_ATTR_REPLICATION) - temp_array[i++] = CStringGetTextDatum("Replication"); + temp_array[i++] = CStringGetTextDatum(_("Replication")); if (attributes & ROLE_ATTR_BYPASSRLS) - temp_array[i++] = CStringGetTextDatum("Bypass RLS"); + temp_array[i++] = CStringGetTextDatum(_("Bypass RLS")); result = construct_array(temp_array, i, TEXTOID, -1, false, 'i'); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index efbcc71..ccb1066 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -33,7 +33,6 @@ #include "access/htup_details.h" #include "access/sysattr.h" #include "access/xact.h" -#include "catalog/pg_authid.h" #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" @@ -44,7 +43,6 @@ #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "miscadmin.h" -#include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/guc.h" diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c index 9af77ed..67d070c 100644 --- a/src/backend/utils/misc/superuser.c +++ b/src/backend/utils/misc/superuser.c @@ -22,7 +22,6 @@ #include "access/htup_details.h" #include "catalog/pg_authid.h" -#include "utils/acl.h" #include "utils/inval.h" #include "utils/syscache.h" #include "miscadmin.h" diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h new file mode 100644 index 0000000..2dcc174 --- /dev/null +++ b/src/include/catalog/acldefs.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * acldefs.h + * base definitions for ACLs and role attributes + * + * Portions Copyright (c) 2014, PostgreSQL Global Development Group + * + * src/include/catalog/acldefs.h + * + *------------------------------------------------------------------------- + */ +#ifndef ACLDEFS_H +#define ACLDEFS_H + +/* + * Grantable rights are encoded so that we can OR them together in a bitmask. + * The present representation of AclItem limits us to 16 distinct rights, + * even though AclMode is defined as uint32. See utils/acl.h. + * + * Caution: changing these codes breaks stored ACLs, hence forces initdb. + */ +typedef uint32 AclMode; /* a bitmask of privilege bits */ + +#define ACL_INSERT (1<<0) /* for relations */ +#define ACL_SELECT (1<<1) +#define ACL_UPDATE (1<<2) +#define ACL_DELETE (1<<3) +#define ACL_TRUNCATE (1<<4) +#define ACL_REFERENCES (1<<5) +#define ACL_TRIGGER (1<<6) +#define ACL_EXECUTE (1<<7) /* for functions */ +#define ACL_USAGE (1<<8) /* for languages, namespaces, FDWs, and + * servers */ +#define ACL_CREATE (1<<9) /* for namespaces and databases */ +#define ACL_CREATE_TEMP (1<<10) /* for databases */ +#define ACL_CONNECT (1<<11) /* for databases */ +#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */ +#define ACL_NO_RIGHTS 0 +/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */ +#define ACL_SELECT_FOR_UPDATE ACL_UPDATE + +#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */ + + +/* + * Role attributes are encoded so that we can OR them together in a bitmask. + * The present representation of RoleAttr (defined in acl.h) limits us to 64 + * distinct rights. + * + * Note about ROLE_ATTR_ALL: This symbol is used verbatim by genbki.pl, which + * means we need to hard-code its value instead of using a symbolic definition. + * Therefore, whenever role attributes are changed, this value MUST be updated + * manually. + */ + +/* A bitmask for role attributes */ +typedef uint64 RoleAttr; + +#define ROLE_ATTR_NONE 0 +#define ROLE_ATTR_SUPERUSER (1<<0) +#define ROLE_ATTR_INHERIT (1<<1) +#define ROLE_ATTR_CREATEROLE (1<<2) +#define ROLE_ATTR_CREATEDB (1<<3) +#define ROLE_ATTR_CATUPDATE (1<<4) +#define ROLE_ATTR_CANLOGIN (1<<5) +#define ROLE_ATTR_REPLICATION (1<<6) +#define ROLE_ATTR_BYPASSRLS (1<<7) +#define N_ROLE_ATTRIBUTES 8 /* 1 plus the last 1<<x */ +#define ROLE_ATTR_ALL 255 /* (1 << N_ROLE_ATTRIBUTES) - 1 */ + + +#endif /* ACLDEFS_H */ diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index f28d9f4..a45f38d 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -21,6 +21,7 @@ #ifndef PG_AUTHID_H #define PG_AUTHID_H +#include "catalog/acldefs.h" #include "catalog/genbki.h" /* @@ -73,34 +74,6 @@ typedef FormData_pg_authid *Form_pg_authid; #define Anum_pg_authid_rolpassword 4 #define Anum_pg_authid_rolvaliduntil 5 -/* ---------------- - * Role attributes are encoded so that we can OR them together in a bitmask. - * The present representation of RoleAttr (defined in acl.h) limits us to 64 - * distinct rights. - * ---------------- - */ -#define ROLE_ATTR_SUPERUSER (1<<0) -#define ROLE_ATTR_INHERIT (1<<1) -#define ROLE_ATTR_CREATEROLE (1<<2) -#define ROLE_ATTR_CREATEDB (1<<3) -#define ROLE_ATTR_CATUPDATE (1<<4) -#define ROLE_ATTR_CANLOGIN (1<<5) -#define ROLE_ATTR_REPLICATION (1<<6) -#define ROLE_ATTR_BYPASSRLS (1<<7) -#define N_ROLE_ATTRIBUTES 8 /* 1 plus the last 1<<x */ -#define ROLE_ATTR_NONE 0 - -/* ---------------- - * All currently available attributes. - * - * Note: This value is currently used by genbki.pl. Unfortunately, we have to - * hard code this value as we cannot use an approach like (1 << N_ROLE_ATTRIBUTES) - 1 - * as genbki.pl simply uses the literal value associated with the #define symbol - * which causes an incorrect substitution. Therefore, whenever new role attributes - * are added this value MUST be changed as well. - * ---------------- - */ -#define ROLE_ATTR_ALL 255 /* equals (1 << N_ROLE_ATTRIBUTES) - 1 */ /* ---------------- * initial contents of pg_authid diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 458eeb0..ecb5780 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -23,8 +23,10 @@ #include "nodes/bitmapset.h" #include "nodes/primnodes.h" #include "nodes/value.h" +#include "catalog/acldefs.h" #include "utils/lockwaitpolicy.h" + /* Possible sources of a Query */ typedef enum QuerySource { @@ -51,33 +53,6 @@ typedef enum SortByNulls SORTBY_NULLS_LAST } SortByNulls; -/* - * Grantable rights are encoded so that we can OR them together in a bitmask. - * The present representation of AclItem limits us to 16 distinct rights, - * even though AclMode is defined as uint32. See utils/acl.h. - * - * Caution: changing these codes breaks stored ACLs, hence forces initdb. - */ -typedef uint32 AclMode; /* a bitmask of privilege bits */ - -#define ACL_INSERT (1<<0) /* for relations */ -#define ACL_SELECT (1<<1) -#define ACL_UPDATE (1<<2) -#define ACL_DELETE (1<<3) -#define ACL_TRUNCATE (1<<4) -#define ACL_REFERENCES (1<<5) -#define ACL_TRIGGER (1<<6) -#define ACL_EXECUTE (1<<7) /* for functions */ -#define ACL_USAGE (1<<8) /* for languages, namespaces, FDWs, and - * servers */ -#define ACL_CREATE (1<<9) /* for namespaces and databases */ -#define ACL_CREATE_TEMP (1<<10) /* for databases */ -#define ACL_CONNECT (1<<11) /* for databases */ -#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */ -#define ACL_NO_RIGHTS 0 -/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */ -#define ACL_SELECT_FOR_UPDATE ACL_UPDATE - /***************************************************************************** * Query Tree diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index 1687633..4e8d81c 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -30,13 +30,6 @@ /* - * typedef AclMode is declared in parsenodes.h, also the individual privilege - * bit meanings are defined there - */ - -#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */ - -/* * AclItem * * Note: must be same size on all platforms, because the size is hardcoded @@ -200,7 +193,6 @@ typedef enum AclObjectKind MAX_ACL_KIND /* MUST BE LAST */ } AclObjectKind; -typedef uint64 RoleAttr; /* a bitmask for role attribute bits */ /* * routines used internally
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 9ceb96b..b0b4fca 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1391,51 +1391,103 @@ </row> <row> - <entry><structfield>rolsuper</structfield></entry> - <entry><type>bool</type></entry> + <entry><structfield>rolattr</structfield></entry> + <entry><type>bigint</type></entry> + <entry> + Role attributes; see <xref linkend="sql-createrole"> for details + </entry> + </row> + + <row> + <entry><structfield>rolconnlimit</structfield></entry> + <entry><type>int4</type></entry> + <entry> + For roles that can log in, this sets maximum number of concurrent + connections this role can make. -1 means no limit. + </entry> + </row> + + <row> + <entry><structfield>rolpassword</structfield></entry> + <entry><type>text</type></entry> + <entry> + Password (possibly encrypted); null if none. If the password + is encrypted, this column will begin with the string <literal>md5</> + followed by a 32-character hexadecimal MD5 hash. The MD5 hash + will be of the user's password concatenated to their user name. + For example, if user <literal>joe</> has password <literal>xyzzy</>, + <productname>PostgreSQL</> will store the md5 hash of + <literal>xyzzyjoe</>. A password that does not follow that + format is assumed to be unencrypted. + </entry> + </row> + + <row> + <entry><structfield>rolvaliduntil</structfield></entry> + <entry><type>timestamptz</type></entry> + <entry>Password expiry time (only used for password authentication); + null if no expiration</entry> + </row> + </tbody> + </tgroup> + </table> + + <table id="catalog-rolattr-bitmap-table"> + <title><structfield>rolattr</> bitmap positions</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Position</entry> + <entry>Attribute</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>0</literal></entry> + <entry>Superuser</entry> <entry>Role has superuser privileges</entry> </row> <row> - <entry><structfield>rolinherit</structfield></entry> - <entry><type>bool</type></entry> - <entry>Role automatically inherits privileges of roles it is a - member of</entry> + <entry><literal>1</literal></entry> + <entry>Inherit</entry> + <entry>Role automatically inherits privileges of roles it is a member of</entry> </row> <row> - <entry><structfield>rolcreaterole</structfield></entry> - <entry><type>bool</type></entry> + <entry><literal>2</literal></entry> + <entry>Create Role</entry> <entry>Role can create more roles</entry> </row> <row> - <entry><structfield>rolcreatedb</structfield></entry> - <entry><type>bool</type></entry> + <entry><literal>3</literal></entry> + <entry>Create DB</entry> <entry>Role can create databases</entry> </row> <row> - <entry><structfield>rolcatupdate</structfield></entry> - <entry><type>bool</type></entry> + <entry><literal>4</literal></entry> + <entry>Catalog Update</entry> <entry> - Role can update system catalogs directly. (Even a superuser cannot do - this unless this column is true) + Role can update system catalogs directly. (Even a superuser cannot do this unless this column is true) </entry> </row> <row> - <entry><structfield>rolcanlogin</structfield></entry> - <entry><type>bool</type></entry> + <entry><literal>5</literal></entry> + <entry>Can Login</entry> <entry> - Role can log in. That is, this role can be given as the initial - session authorization identifier + Role can log in. That is, this role can be given as the initial session authorization identifier </entry> </row> <row> - <entry><structfield>rolreplication</structfield></entry> - <entry><type>bool</type></entry> + <entry><literal>6</literal></entry> + <entry>Replication</entry> <entry> Role is a replication role. That is, this role can initiate streaming replication (see <xref linkend="streaming-replication">) and set/unset @@ -1445,35 +1497,13 @@ </row> <row> - <entry><structfield>rolconnlimit</structfield></entry> - <entry><type>int4</type></entry> - <entry> - For roles that can log in, this sets maximum number of concurrent - connections this role can make. -1 means no limit. - </entry> - </row> - - <row> - <entry><structfield>rolpassword</structfield></entry> - <entry><type>text</type></entry> + <entry><literal>7</literal></entry> + <entry>By-pass Row Level Security</entry> <entry> - Password (possibly encrypted); null if none. If the password - is encrypted, this column will begin with the string <literal>md5</> - followed by a 32-character hexadecimal MD5 hash. The MD5 hash - will be of the user's password concatenated to their user name. - For example, if user <literal>joe</> has password <literal>xyzzy</>, - <productname>PostgreSQL</> will store the md5 hash of - <literal>xyzzyjoe</>. A password that does not follow that - format is assumed to be unencrypted. + Role can by-pass row level security policies when <literal>row_security</> is set <literal>off</> </entry> </row> - <row> - <entry><structfield>rolvaliduntil</structfield></entry> - <entry><type>timestamptz</type></entry> - <entry>Password expiry time (only used for password authentication); - null if no expiration</entry> - </row> </tbody> </tgroup> </table> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7934a12..20b8325 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -15139,6 +15139,123 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute'); are immediately available without doing <command>SET ROLE</>. </para> + <para> + <xref linkend="functions-info-role-attribute-table"> lists functions that + allow the user to query role attribute information programmatically. + </para> + + <table id="functions-info-role-attribute-table"> + <title>Role Attribute Inquiry Functions</title> + <tgroup cols="3"> + <thead> + <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row> + </thead> + <tbody> + <row> + <entry><literal><function>pg_has_role_attribute(role, attribute)</function></literal></entry> + <entry><type>boolean</type></entry> + <entry>does role have the permissions allowed by named attribute</entry> + </row> + <row> + <entry><literal><function>pg_check_role_attribute(role, attribute)</function></literal></entry> + <entry><type>boolean</type></entry> + <entry>does role have the named attribute</entry> + </row> + <row> + <entry><literal><function>pg_check_role_attribute(role_attributes, attribute)</function></literal></entry> + <entry><type>boolean</type></entry> + <entry>is attribute set in bitmap of role attributes</entry> + </row> + <row> + <entry><literal><function>pg_all_role_attributes(role_attributes)</function></literal></entry> + <entry><type>boolean</type></entry> + <entry>convert bitmap of role attribute representation to string array</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + <function>pg_has_role_attribute</function> checks the attribute permissions + given to a role. It will always return <literal>true</literal> for roles + with superuser privileges unless the attribute being checked is + <literal>CATUPDATE</literal> (superuser cannot bypass + <literal>CATUPDATE</literal> permissions). The role can be specified by name + and by OID. The attribute is specified by a text string which must evaluate + to one of the following role attributes: + <literal>SUPERUSER</literal>, + <literal>INHERIT</literal>, + <literal>CREATEROLE</literal>, + <literal>CREATEDB</literal>, + <literal>CATUPDATE</literal>, + <literal>CANLOGIN</literal>, + <literal>REPLICATION</literal>, or + <literal>BYPASSRLS</literal>. + Example: +<programlisting> +SELECT pg_has_role_attribute('joe', 'SUPERUSER'); + pg_has_role_attribute +----------------------- + f +(1 row) + +SELECT rolname, pg_has_role_attribute(oid, 'INHERIT') AS rolinherit FROM pg_roles; + rolname | rolinherit +----------+------------ + postgres | t + joe | t +(2 rows) +</programlisting> + </para> + + <para> + <function>pg_check_role_attribute</function> check the attribute value given + to a role. The role can be specified by name and by OID. The attribute is + specified by a text string which must evaluate to a valid role attribute (see + <function>pg_has_role_attribute</function>). A third variant of this function + allows for a bitmap representation (<literal>bigint</literal>) of attributes + to be given instead of a role. + Example: +<programlisting> +SELECT pg_check_role_attribute('joe', 'SUPERUSER'); + pg_check_role_attribute +------------------------- + f +(1 row) + +SELECT rolname, pg_check_role_attribute(oid, 'INHERIT') as rolinherit FROM pg_roles; + rolname | rolinherit +----------+------------ + postgres | t + joe | t +(2 rows) + t +(1 row) + + +SELECT rolname, pg_check_role_attribute(rolattr, 'SUPERUSER') AS rolsuper FROM pg_authid; + rolname | rolsuper +----------+---------- + postgres | t + joe | f +(2 rows) +</programlisting> + </para> + + <para> + <function>pg_all_role_attributes</function> convert a set of role attributes + represented by an <literal>bigint</literal> bitmap to a string array. + Example: +<programlisting> +SELECT rolname, pg_all_role_attributes(rolattr) AS attributes FROM pg_authid; + rolname | attributes +----------+----------------------------------------------------------------------------------------------- + postgres | {Superuser,Inherit,"Create Role","Create DB","Catalog Update",Login,Replication,"Bypass RLS"} + joe | {Inherit,Login} +(2 rows) +</programlisting> + </para> + <para> <xref linkend="functions-info-schema-table"> shows functions that determine whether a certain object is <firstterm>visible</> in the diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 133143d..3181a79 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -27,6 +27,7 @@ #include "miscadmin.h" #include "replication/walreceiver.h" #include "storage/smgr.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/numeric.h" #include "utils/guc.h" @@ -54,7 +55,7 @@ pg_start_backup(PG_FUNCTION_ARGS) backupidstr = text_to_cstring(backupid); - if (!superuser() && !has_rolreplication(GetUserId())) + if (!have_role_attribute(ROLE_ATTR_REPLICATION)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to run a backup"))); @@ -82,7 +83,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) { XLogRecPtr stoppoint; - if (!superuser() && !has_rolreplication(GetUserId())) + if (!have_role_attribute(ROLE_ATTR_REPLICATION)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to run a backup")))); diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index a403c64..a6de2ff 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -28,7 +28,7 @@ all: $(BKIFILES) schemapg.h # indexing.h had better be last, and toasting.h just before it. POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ - pg_proc.h pg_type.h pg_attribute.h pg_class.h \ + acldefs.h pg_proc.h pg_type.h pg_attribute.h pg_class.h \ pg_attrdef.h pg_constraint.h pg_inherits.h pg_index.h pg_operator.h \ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject_metadata.h pg_largeobject.h pg_aggregate.h \ diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index d30612c..21d282c 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -3423,26 +3423,6 @@ aclcheck_error_type(AclResult aclerr, Oid typeOid) } -/* Check if given user has rolcatupdate privilege according to pg_authid */ -static bool -has_rolcatupdate(Oid roleid) -{ - bool rolcatupdate; - HeapTuple tuple; - - tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (!HeapTupleIsValid(tuple)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("role with OID %u does not exist", roleid))); - - rolcatupdate = ((Form_pg_authid) GETSTRUCT(tuple))->rolcatupdate; - - ReleaseSysCache(tuple); - - return rolcatupdate; -} - /* * Relay for the various pg_*_mask routines depending on object kind */ @@ -3630,7 +3610,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid, if ((mask & (ACL_INSERT | ACL_UPDATE | ACL_DELETE | ACL_TRUNCATE | ACL_USAGE)) && IsSystemClass(table_oid, classForm) && classForm->relkind != RELKIND_VIEW && - !has_rolcatupdate(roleid) && + !has_role_attribute(roleid, ROLE_ATTR_CATUPDATE) && !allowSystemTableMods) { #ifdef ACLDEBUG @@ -5051,52 +5031,80 @@ pg_extension_ownercheck(Oid ext_oid, Oid roleid) } /* - * Check whether specified role has CREATEROLE privilege (or is a superuser) + * has_role_attribute + * Check if the role with the specified id has been assigned a specific role + * attribute. + * + * roleid - the oid of the role to check. + * attribute - the attribute to check. + * + * Note: Use this function for role attribute permission checking as it + * accounts for superuser status. It will always return true for roles with + * superuser privileges unless the attribute being checked is CATUPDATE + * (superusers are not allowed to bypass CATUPDATE permissions). * - * Note: roles do not have owners per se; instead we use this test in - * places where an ownership-like permissions test is needed for a role. - * Be sure to apply it to the role trying to do the operation, not the - * role being operated on! Also note that this generally should not be - * considered enough privilege if the target role is a superuser. - * (We don't handle that consideration here because we want to give a - * separate error message for such cases, so the caller has to deal with it.) + * Note: roles do not have owners per se; instead we use this test in places + * where an ownership-like permissions test is needed for a role. Be sure to + * apply it to the role trying to do the operation, not the role being operated + * on! Also note that this generally should not be considered enough privilege + * if the target role is a superuser. (We don't handle that consideration here + * because we want to give a separate error message for such cases, so the + * caller has to deal with it.) */ bool -has_createrole_privilege(Oid roleid) +has_role_attribute(Oid roleid, RoleAttr attribute) { - bool result = false; - HeapTuple utup; - - /* Superusers bypass all permission checking. */ - if (superuser_arg(roleid)) + /* + * Superusers bypass all permission checking except in the case of CATUPDATE. + */ + if (!(attribute & ROLE_ATTR_CATUPDATE) && superuser_arg(roleid)) return true; - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreaterole; - ReleaseSysCache(utup); - } - return result; + return check_role_attribute(roleid, attribute); +} + +/* + * have_role_attribute + * Convenience function for checking if the role id returned by GetUserId() has + * been assigned a specific role attribute. + * + * attribute - the attribute to check. + */ +bool +have_role_attribute(RoleAttr attribute) +{ + return has_role_attribute(GetUserId(), attribute); } +/* + * check_role_attribute + * Check if the role with the specified id has been assigned a specific role + * attribute. + * + * roleid - the oid of the role to check. + * attribute - the attribute to check. + * + * Note: This function should only be used for checking the value of an individual + * attribute in the rolattr bitmap and should *not* be used for permission checking. + * For the purposes of permission checking use 'has_role_attribute' instead. + */ bool -has_bypassrls_privilege(Oid roleid) +check_role_attribute(Oid roleid, RoleAttr attribute) { - bool result = false; - HeapTuple utup; + RoleAttr attributes; + HeapTuple tuple; - /* Superusers bypass all permission checking. */ - if (superuser_arg(roleid)) - return true; + tuple = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolbypassrls; - ReleaseSysCache(utup); - } - return result; + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("role with OID %u does not exist", roleid))); + + attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr; + ReleaseSysCache(tuple); + + return (attributes & attribute); } /* diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index ca89879..415ac17 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -90,6 +90,8 @@ my $BOOTSTRAP_SUPERUSERID = find_defined_symbol('pg_authid.h', 'BOOTSTRAP_SUPERUSERID'); my $PG_CATALOG_NAMESPACE = find_defined_symbol('pg_namespace.h', 'PG_CATALOG_NAMESPACE'); +my $ROLE_ATTR_ALL = + find_defined_symbol('acldefs.h', 'ROLE_ATTR_ALL'); # Read all the input header files into internal data structures my $catalogs = Catalog::Catalogs(@input_files); @@ -144,6 +146,7 @@ foreach my $catname (@{ $catalogs->{names} }) # substitute constant values we acquired above $row->{bki_values} =~ s/\bPGUID\b/$BOOTSTRAP_SUPERUSERID/g; $row->{bki_values} =~ s/\bPGNSP\b/$PG_CATALOG_NAMESPACE/g; + $row->{bki_values} =~ s/\bPGROLATTRALL/$ROLE_ATTR_ALL/g; # Save pg_type info for pg_attribute processing below if ($catname eq 'pg_type') diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index a036c62..87b6d8c 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -2884,7 +2884,12 @@ CREATE VIEW user_mapping_options AS CAST((pg_options_to_table(um.umoptions)).option_name AS sql_identifier) AS option_name, CAST(CASE WHEN (umuser <> 0 AND authorization_identifier = current_user) OR (umuser = 0 AND pg_has_role(srvowner, 'USAGE')) - OR (SELECT rolsuper FROM pg_authid WHERE rolname = current_user) THEN (pg_options_to_table(um.umoptions)).option_value + OR ( + SELECT pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper + FROM pg_authid + WHERE rolname = current_user + ) + THEN (pg_options_to_table(um.umoptions)).option_value ELSE NULL END AS character_data) AS option_value FROM _pg_user_mappings um; diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index e261307..6df7e4d 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -1310,7 +1310,7 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address, } else { - if (!has_createrole_privilege(roleid)) + if (!has_role_attribute(roleid, ROLE_ATTR_CREATEROLE)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must have CREATEROLE privilege"))); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 22b8cee..ae93832 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -9,17 +9,17 @@ CREATE VIEW pg_roles AS SELECT rolname, - rolsuper, - rolinherit, - rolcreaterole, - rolcreatedb, - rolcatupdate, - rolcanlogin, - rolreplication, + pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS rolsuper, + pg_check_role_attribute(pg_authid.rolattr, 'INHERIT') AS rolinherit, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE') AS rolcreaterole, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS rolcreatedb, + pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS rolcatupdate, + pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN') AS rolcanlogin, + pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS rolreplication, + pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS') AS rolbypassrls, rolconnlimit, '********'::text as rolpassword, rolvaliduntil, - rolbypassrls, setconfig as rolconfig, pg_authid.oid FROM pg_authid LEFT JOIN pg_db_role_setting s @@ -29,16 +29,16 @@ CREATE VIEW pg_shadow AS SELECT rolname AS usename, pg_authid.oid AS usesysid, - rolcreatedb AS usecreatedb, - rolsuper AS usesuper, - rolcatupdate AS usecatupd, - rolreplication AS userepl, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB') AS usecreatedb, + pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER') AS usesuper, + pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE') AS usecatupd, + pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION') AS userepl, rolpassword AS passwd, rolvaliduntil::abstime AS valuntil, setconfig AS useconfig FROM pg_authid LEFT JOIN pg_db_role_setting s ON (pg_authid.oid = setrole AND setdatabase = 0) - WHERE rolcanlogin; + WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'); REVOKE ALL on pg_shadow FROM public; @@ -48,7 +48,7 @@ CREATE VIEW pg_group AS oid AS grosysid, ARRAY(SELECT member FROM pg_auth_members WHERE roleid = oid) AS grolist FROM pg_authid - WHERE NOT rolcanlogin; + WHERE NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'); CREATE VIEW pg_user AS SELECT diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 1a5244c..c079168 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -85,7 +85,6 @@ static bool get_db_info(const char *name, LOCKMODE lockmode, Oid *dbLastSysOidP, TransactionId *dbFrozenXidP, MultiXactId *dbMinMultiP, Oid *dbTablespace, char **dbCollate, char **dbCtype); -static bool have_createdb_privilege(void); static void remove_dbtablespaces(Oid db_id); static bool check_db_file_conflict(Oid db_id); static int errdetail_busy_db(int notherbackends, int npreparedxacts); @@ -291,7 +290,7 @@ createdb(const CreatedbStmt *stmt) * "giveaway" attacks. Note that a superuser will always have both of * these privileges a fortiori. */ - if (!have_createdb_privilege()) + if (!have_role_attribute(ROLE_ATTR_CREATEDB)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to create database"))); @@ -965,7 +964,7 @@ RenameDatabase(const char *oldname, const char *newname) oldname); /* must have createdb rights */ - if (!have_createdb_privilege()) + if (!have_role_attribute(ROLE_ATTR_CREATEDB)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename database"))); @@ -1623,7 +1622,7 @@ AlterDatabaseOwner(const char *dbname, Oid newOwnerId) * databases. Because superusers will always have this right, we need * no special case for them. */ - if (!have_createdb_privilege()) + if (!have_role_attribute(ROLE_ATTR_CREATEDB)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to change owner of database"))); @@ -1802,26 +1801,6 @@ get_db_info(const char *name, LOCKMODE lockmode, return result; } -/* Check if current user has createdb privileges */ -static bool -have_createdb_privilege(void) -{ - bool result = false; - HeapTuple utup; - - /* Superusers can always do everything */ - if (superuser()) - return true; - - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(GetUserId())); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolcreatedb; - ReleaseSysCache(utup); - } - return result; -} - /* * Remove tablespace directories * diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c index 1a73fd8..564f77a 100644 --- a/src/backend/commands/user.c +++ b/src/backend/commands/user.c @@ -56,14 +56,6 @@ static void DelRoleMems(const char *rolename, Oid roleid, bool admin_opt); -/* Check if current user has createrole privileges */ -static bool -have_createrole_privilege(void) -{ - return has_createrole_privilege(GetUserId()); -} - - /* * CREATE ROLE */ @@ -81,13 +73,7 @@ CreateRole(CreateRoleStmt *stmt) char *password = NULL; /* user password */ bool encrypt_password = Password_encryption; /* encrypt password? */ char encrypted_password[MD5_PASSWD_LEN + 1]; - bool issuper = false; /* Make the user a superuser? */ - bool inherit = true; /* Auto inherit privileges? */ - bool createrole = false; /* Can this user create roles? */ - bool createdb = false; /* Can the user create databases? */ - bool canlogin = false; /* Can this user login? */ - bool isreplication = false; /* Is this a replication role? */ - bool bypassrls = false; /* Is this a row security enabled role? */ + RoleAttr attributes; int connlimit = -1; /* maximum connections allowed */ List *addroleto = NIL; /* roles to make this a member of */ List *rolemembers = NIL; /* roles to be members of this role */ @@ -109,13 +95,17 @@ CreateRole(CreateRoleStmt *stmt) DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; - /* The defaults can vary depending on the original statement type */ + /* + * Every role has INHERIT by default, and CANLOGIN depends on the statement + * type. + */ + attributes = ROLE_ATTR_INHERIT; switch (stmt->stmt_type) { case ROLESTMT_ROLE: break; case ROLESTMT_USER: - canlogin = true; + attributes |= ROLE_ATTR_CANLOGIN; /* may eventually want inherit to default to false here */ break; case ROLESTMT_GROUP: @@ -249,18 +239,76 @@ CreateRole(CreateRoleStmt *stmt) if (dpassword && dpassword->arg) password = strVal(dpassword->arg); + + /* Set up role attributes and check permissions to set each of them */ if (dissuper) - issuper = intVal(dissuper->arg) != 0; + { + if (intVal(dissuper->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create superusers"))); + attributes |= ROLE_ATTR_SUPERUSER; + } + else + attributes &= ~ROLE_ATTR_SUPERUSER; + } if (dinherit) - inherit = intVal(dinherit->arg) != 0; + { + if (intVal(dinherit->arg) != 0) + attributes |= ROLE_ATTR_INHERIT; + else + attributes &= ~ROLE_ATTR_INHERIT; + } if (dcreaterole) - createrole = intVal(dcreaterole->arg) != 0; + { + if (intVal(dcreaterole->arg) != 0) + attributes |= ROLE_ATTR_CREATEROLE; + else + attributes &= ~ROLE_ATTR_CREATEROLE; + } if (dcreatedb) - createdb = intVal(dcreatedb->arg) != 0; + { + if (intVal(dcreatedb->arg) != 0) + attributes |= ROLE_ATTR_CREATEDB; + else + attributes &= ~ROLE_ATTR_CREATEDB; + } if (dcanlogin) - canlogin = intVal(dcanlogin->arg) != 0; + { + if (intVal(dcanlogin->arg) != 0) + attributes |= ROLE_ATTR_CANLOGIN; + else + attributes &= ~ROLE_ATTR_CANLOGIN; + } if (disreplication) - isreplication = intVal(disreplication->arg) != 0; + { + if (intVal(disreplication->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to create replication users"))); + attributes |= ROLE_ATTR_REPLICATION; + } + else + attributes &= ~ROLE_ATTR_REPLICATION; + } + if (dbypassRLS) + { + if (intVal(dbypassRLS->arg) != 0) + { + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to change bypassrls attribute"))); + attributes |= ROLE_ATTR_BYPASSRLS; + } + else + attributes &= ~ROLE_ATTR_BYPASSRLS; + } + if (dconnlimit) { connlimit = intVal(dconnlimit->arg); @@ -277,38 +325,12 @@ CreateRole(CreateRoleStmt *stmt) adminmembers = (List *) dadminmembers->arg; if (dvalidUntil) validUntil = strVal(dvalidUntil->arg); - if (dbypassRLS) - bypassrls = intVal(dbypassRLS->arg) != 0; - /* Check some permissions first */ - if (issuper) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create superusers"))); - } - else if (isreplication) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to create replication users"))); - } - else if (bypassrls) - { - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to change bypassrls attribute."))); - } - else - { - if (!have_createrole_privilege()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("permission denied to create role"))); - } + /* Check permissions */ + if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create role"))); if (strcmp(stmt->role, "public") == 0 || strcmp(stmt->role, "none") == 0) @@ -364,14 +386,8 @@ CreateRole(CreateRoleStmt *stmt) new_record[Anum_pg_authid_rolname - 1] = DirectFunctionCall1(namein, CStringGetDatum(stmt->role)); - new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper); - new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit); - new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole); - new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb); - /* superuser gets catupdate right by default */ - new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper); - new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin); - new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication); + new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes); + new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); if (password) @@ -394,8 +410,6 @@ CreateRole(CreateRoleStmt *stmt) new_record[Anum_pg_authid_rolvaliduntil - 1] = validUntil_datum; new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; - new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls); - tuple = heap_form_tuple(pg_authid_dsc, new_record, new_record_nulls); /* @@ -508,6 +522,7 @@ AlterRole(AlterRoleStmt *stmt) DefElem *dvalidUntil = NULL; DefElem *dbypassRLS = NULL; Oid roleid; + RoleAttr attributes; /* Extract options from the statement node tree */ foreach(option, stmt->options) @@ -658,31 +673,34 @@ AlterRole(AlterRoleStmt *stmt) roleid = HeapTupleGetOid(tuple); /* - * To mess with a superuser you gotta be superuser; else you need - * createrole, or just want to change your own password + * To mess with a superuser or a replication user you gotta be superuser; + * else you need createrole, or just want to change your own password */ - if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper || issuper >= 0) + + attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr; + + if ((attributes & ROLE_ATTR_SUPERUSER) || issuper >= 0) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to alter superusers"))); } - else if (((Form_pg_authid) GETSTRUCT(tuple))->rolreplication || isreplication >= 0) + else if ((attributes & ROLE_ATTR_REPLICATION) || isreplication >= 0) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to alter replication users"))); } - else if (((Form_pg_authid) GETSTRUCT(tuple))->rolbypassrls || bypassrls >= 0) + else if ((attributes & ROLE_ATTR_BYPASSRLS) || bypassrls >= 0) { if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to change bypassrls attribute"))); } - else if (!have_createrole_privilege()) + else if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) { if (!(inherit < 0 && createrole < 0 && @@ -743,43 +761,71 @@ AlterRole(AlterRoleStmt *stmt) */ if (issuper >= 0) { - new_record[Anum_pg_authid_rolsuper - 1] = BoolGetDatum(issuper > 0); - new_record_repl[Anum_pg_authid_rolsuper - 1] = true; - - new_record[Anum_pg_authid_rolcatupdate - 1] = BoolGetDatum(issuper > 0); - new_record_repl[Anum_pg_authid_rolcatupdate - 1] = true; + if (issuper > 0) + attributes |= ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE; + else + attributes &= ~(ROLE_ATTR_SUPERUSER | ROLE_ATTR_CATUPDATE); + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (inherit >= 0) { - new_record[Anum_pg_authid_rolinherit - 1] = BoolGetDatum(inherit > 0); - new_record_repl[Anum_pg_authid_rolinherit - 1] = true; + if (inherit > 0) + attributes |= ROLE_ATTR_INHERIT; + else + attributes &= ~ROLE_ATTR_INHERIT; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (createrole >= 0) { - new_record[Anum_pg_authid_rolcreaterole - 1] = BoolGetDatum(createrole > 0); - new_record_repl[Anum_pg_authid_rolcreaterole - 1] = true; + if (createrole > 0) + attributes |= ROLE_ATTR_CREATEROLE; + else + attributes &= ~ROLE_ATTR_CREATEROLE; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (createdb >= 0) { - new_record[Anum_pg_authid_rolcreatedb - 1] = BoolGetDatum(createdb > 0); - new_record_repl[Anum_pg_authid_rolcreatedb - 1] = true; + if (createdb > 0) + attributes |= ROLE_ATTR_CREATEDB; + else + attributes &= ~ROLE_ATTR_CREATEDB; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (canlogin >= 0) { - new_record[Anum_pg_authid_rolcanlogin - 1] = BoolGetDatum(canlogin > 0); - new_record_repl[Anum_pg_authid_rolcanlogin - 1] = true; + if (canlogin > 0) + attributes |= ROLE_ATTR_CANLOGIN; + else + attributes &= ~ROLE_ATTR_CANLOGIN; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } if (isreplication >= 0) { - new_record[Anum_pg_authid_rolreplication - 1] = BoolGetDatum(isreplication > 0); - new_record_repl[Anum_pg_authid_rolreplication - 1] = true; + if (isreplication > 0) + attributes |= ROLE_ATTR_REPLICATION; + else + attributes &= ~ROLE_ATTR_REPLICATION; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; } + if (bypassrls >= 0) + { + if (bypassrls > 0) + attributes |= ROLE_ATTR_BYPASSRLS; + else + attributes &= ~ROLE_ATTR_BYPASSRLS; + new_record_repl[Anum_pg_authid_rolattr - 1] = true; + } + + /* If any role attributes were set, then update. */ + if (new_record_repl[Anum_pg_authid_rolattr - 1]) + new_record[Anum_pg_authid_rolattr - 1] = Int64GetDatum(attributes); + if (dconnlimit) { new_record[Anum_pg_authid_rolconnlimit - 1] = Int32GetDatum(connlimit); @@ -815,11 +861,6 @@ AlterRole(AlterRoleStmt *stmt) new_record_nulls[Anum_pg_authid_rolvaliduntil - 1] = validUntil_null; new_record_repl[Anum_pg_authid_rolvaliduntil - 1] = true; - if (bypassrls >= 0) - { - new_record[Anum_pg_authid_rolbypassrls - 1] = BoolGetDatum(bypassrls > 0); - new_record_repl[Anum_pg_authid_rolbypassrls - 1] = true; - } new_tuple = heap_modify_tuple(tuple, pg_authid_dsc, new_record, new_record_nulls, new_record_repl); @@ -867,6 +908,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt) HeapTuple roletuple; Oid databaseid = InvalidOid; Oid roleid = InvalidOid; + RoleAttr attributes; if (stmt->role) { @@ -889,7 +931,8 @@ AlterRoleSet(AlterRoleSetStmt *stmt) * To mess with a superuser you gotta be superuser; else you need * createrole, or just want to change your own settings */ - if (((Form_pg_authid) GETSTRUCT(roletuple))->rolsuper) + attributes = ((Form_pg_authid) GETSTRUCT(roletuple))->rolattr; + if (attributes & ROLE_ATTR_SUPERUSER) { if (!superuser()) ereport(ERROR, @@ -898,7 +941,7 @@ AlterRoleSet(AlterRoleSetStmt *stmt) } else { - if (!have_createrole_privilege() && + if (!have_role_attribute(ROLE_ATTR_CREATEROLE) && HeapTupleGetOid(roletuple) != GetUserId()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -951,7 +994,7 @@ DropRole(DropRoleStmt *stmt) pg_auth_members_rel; ListCell *item; - if (!have_createrole_privilege()) + if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to drop role"))); @@ -973,6 +1016,7 @@ DropRole(DropRoleStmt *stmt) char *detail_log; SysScanDesc sscan; Oid roleid; + RoleAttr attributes; tuple = SearchSysCache1(AUTHNAME, PointerGetDatum(role)); if (!HeapTupleIsValid(tuple)) @@ -1013,8 +1057,8 @@ DropRole(DropRoleStmt *stmt) * roles but not superuser roles. This is mainly to avoid the * scenario where you accidentally drop the last superuser. */ - if (((Form_pg_authid) GETSTRUCT(tuple))->rolsuper && - !superuser()) + attributes = ((Form_pg_authid) GETSTRUCT(tuple))->rolattr; + if ((attributes & ROLE_ATTR_SUPERUSER) && !superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to drop superusers"))); @@ -1128,6 +1172,7 @@ RenameRole(const char *oldname, const char *newname) bool repl_repl[Natts_pg_authid]; int i; Oid roleid; + RoleAttr attributes; rel = heap_open(AuthIdRelationId, RowExclusiveLock); dsc = RelationGetDescr(rel); @@ -1173,7 +1218,8 @@ RenameRole(const char *oldname, const char *newname) /* * createrole is enough privilege unless you want to mess with a superuser */ - if (((Form_pg_authid) GETSTRUCT(oldtuple))->rolsuper) + attributes = ((Form_pg_authid) GETSTRUCT(oldtuple))->rolattr; + if (attributes & ROLE_ATTR_SUPERUSER) { if (!superuser()) ereport(ERROR, @@ -1182,7 +1228,7 @@ RenameRole(const char *oldname, const char *newname) } else { - if (!have_createrole_privilege()) + if (!have_role_attribute(ROLE_ATTR_CREATEROLE)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied to rename role"))); @@ -1409,7 +1455,7 @@ AddRoleMems(const char *rolename, Oid roleid, } else { - if (!have_createrole_privilege() && + if (!have_role_attribute(ROLE_ATTR_CREATEROLE) && !is_admin_of_role(grantorId, roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), @@ -1555,7 +1601,7 @@ DelRoleMems(const char *rolename, Oid roleid, } else { - if (!have_createrole_privilege() && + if (!have_role_attribute(ROLE_ATTR_CREATEROLE) && !is_admin_of_role(GetUserId(), roleid)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), diff --git a/src/backend/commands/variable.c b/src/backend/commands/variable.c index 6ce8dae..491dc38 100644 --- a/src/backend/commands/variable.c +++ b/src/backend/commands/variable.c @@ -776,6 +776,7 @@ check_session_authorization(char **newval, void **extra, GucSource source) Oid roleid; bool is_superuser; role_auth_extra *myextra; + RoleAttr attributes; /* Do nothing for the boot_val default of NULL */ if (*newval == NULL) @@ -800,7 +801,8 @@ check_session_authorization(char **newval, void **extra, GucSource source) } roleid = HeapTupleGetOid(roleTup); - is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper; + attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr; + is_superuser = (attributes & ROLE_ATTR_SUPERUSER); ReleaseSysCache(roleTup); @@ -844,6 +846,7 @@ check_role(char **newval, void **extra, GucSource source) Oid roleid; bool is_superuser; role_auth_extra *myextra; + RoleAttr attributes; if (strcmp(*newval, "none") == 0) { @@ -872,7 +875,8 @@ check_role(char **newval, void **extra, GucSource source) } roleid = HeapTupleGetOid(roleTup); - is_superuser = ((Form_pg_authid) GETSTRUCT(roleTup))->rolsuper; + attributes = ((Form_pg_authid) GETSTRUCT(roleTup))->rolattr; + is_superuser = (attributes & ROLE_ATTR_SUPERUSER); ReleaseSysCache(roleTup); diff --git a/src/backend/replication/logical/logicalfuncs.c b/src/backend/replication/logical/logicalfuncs.c index 1977f09..1a38f56 100644 --- a/src/backend/replication/logical/logicalfuncs.c +++ b/src/backend/replication/logical/logicalfuncs.c @@ -17,18 +17,14 @@ #include <unistd.h> +#include "access/xlog_internal.h" +#include "catalog/pg_type.h" #include "fmgr.h" #include "funcapi.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" - -#include "access/xlog_internal.h" - -#include "catalog/pg_type.h" - #include "nodes/makefuncs.h" - -#include "mb/pg_wchar.h" - +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/inval.h" @@ -36,11 +32,9 @@ #include "utils/pg_lsn.h" #include "utils/resowner.h" #include "utils/lsyscache.h" - #include "replication/decode.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" - #include "storage/fd.h" /* private date for writing out data */ @@ -205,7 +199,7 @@ XLogRead(char *buf, TimeLineID tli, XLogRecPtr startptr, Size count) static void check_permissions(void) { - if (!superuser() && !has_rolreplication(GetUserId())) + if (!have_role_attribute(ROLE_ATTR_REPLICATION)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); diff --git a/src/backend/replication/slotfuncs.c b/src/backend/replication/slotfuncs.c index bd4701f..c113a0b 100644 --- a/src/backend/replication/slotfuncs.c +++ b/src/backend/replication/slotfuncs.c @@ -20,13 +20,14 @@ #include "replication/slot.h" #include "replication/logical.h" #include "replication/logicalfuncs.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/pg_lsn.h" static void check_permissions(void) { - if (!superuser() && !has_rolreplication(GetUserId())) + if (!have_role_attribute(ROLE_ATTR_REPLICATION)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser or replication role to use replication slots")))); diff --git a/src/backend/rewrite/rowsecurity.c b/src/backend/rewrite/rowsecurity.c index 6c232dc..f41ad34 100644 --- a/src/backend/rewrite/rowsecurity.c +++ b/src/backend/rewrite/rowsecurity.c @@ -521,7 +521,7 @@ check_enable_rls(Oid relid, Oid checkAsUser) */ if (!checkAsUser && row_security == ROW_SECURITY_OFF) { - if (has_bypassrls_privilege(user_id)) + if (has_role_attribute(user_id, ROLE_ATTR_BYPASSRLS)) /* OK to bypass */ return RLS_NONE_ENV; else diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index dc6eb2c..4c03955 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -115,6 +115,7 @@ static Oid convert_type_name(text *typename); static AclMode convert_type_priv_string(text *priv_type_text); static AclMode convert_role_priv_string(text *priv_type_text); static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode); +static RoleAttr convert_role_attr_string(text *attr_type_text); static void RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue); @@ -4602,6 +4603,186 @@ pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode) return ACLCHECK_NO_PRIV; } +/* + * pg_has_role_attribute_id + * Check that the role with the given oid has the given named role + * attribute. + * + * Note: This function applies superuser checks. Therefore, if the provided + * role is a superuser, then the result will always be true. + */ +Datum +pg_has_role_attribute_id(PG_FUNCTION_ARGS) +{ + Oid roleoid = PG_GETARG_OID(0); + text *attr_type_text = PG_GETARG_TEXT_P(1); + RoleAttr attribute; + + attribute = convert_role_attr_string(attr_type_text); + + PG_RETURN_BOOL(has_role_attribute(roleoid, attribute)); +} + +/* + * pg_has_role_attribute_name + * Check that the named role has the given named role attribute. + * + * Note: This function applies superuser checks. Therefore, if the provided + * role is a superuser, then the result will always be true. + */ +Datum +pg_has_role_attribute_name(PG_FUNCTION_ARGS) +{ + Name rolename = PG_GETARG_NAME(0); + text *attr_type_text = PG_GETARG_TEXT_P(1); + Oid roleoid; + RoleAttr attribute; + + roleoid = get_role_oid(NameStr(*rolename), false); + attribute = convert_role_attr_string(attr_type_text); + + PG_RETURN_BOOL(has_role_attribute(roleoid, attribute)); +} + +/* + * pg_check_role_attribute_id + * Check that the role with the given oid has the given named role + * attribute. + * + * Note: This function is different from 'pg_has_role_attribute_id_attr' in that + * it does *not* apply any superuser checks. Therefore, this function will + * always return the set value of the attribute, despite the superuser-ness of + * the provided role. + */ +Datum +pg_check_role_attribute_id(PG_FUNCTION_ARGS) +{ + Oid roleoid = PG_GETARG_OID(0); + text *attr_type_text = PG_GETARG_TEXT_P(1); + RoleAttr attribute; + + attribute = convert_role_attr_string(attr_type_text); + + PG_RETURN_BOOL(check_role_attribute(roleoid, attribute)); +} + +/* + * pg_check_role_attribute_name + * Check that the named role has the given named role attribute. + * + * Note: This function is different from 'pg_has_role_attribute_name_attr' in + * that it does *not* apply any superuser checks. Therefore, this function will + * always return the set value of the attribute, despite the superuser-ness of + * the provided role. + */ +Datum +pg_check_role_attribute_name(PG_FUNCTION_ARGS) +{ + Name rolename = PG_GETARG_NAME(0); + text *attr_type_text = PG_GETARG_TEXT_P(1); + Oid roleoid; + RoleAttr attribute; + + roleoid = get_role_oid(NameStr(*rolename), false); + attribute = convert_role_attr_string(attr_type_text); + + PG_RETURN_BOOL(check_role_attribute(roleoid, attribute)); +} + +/* + * pg_check_role_attribute_attrs + * Check that the named attribute is enabled in the given RoleAttr + * representation of role attributes. + */ +Datum +pg_check_role_attribute_attrs(PG_FUNCTION_ARGS) +{ + RoleAttr attributes = PG_GETARG_INT64(0); + text *attr_type_text = PG_GETARG_TEXT_P(1); + RoleAttr attribute; + + attribute = convert_role_attr_string(attr_type_text); + + PG_RETURN_BOOL(attributes & attribute); +} + +/* + * pg_all_role_attributes + * Convert a RoleAttr representation of role attributes into an array of + * corresponding text values. + * + * The first and only argument is a RoleAttr (int64) representation of the + * role attributes. + */ +Datum +pg_all_role_attributes(PG_FUNCTION_ARGS) +{ + RoleAttr attributes = PG_GETARG_INT64(0); + Datum *temp_array; + ArrayType *result; + int i = 0; + + /* + * Short-circuit the case for no attributes assigned. + */ + if (attributes == ROLE_ATTR_NONE) + PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID)); + + temp_array = (Datum *) palloc(N_ROLE_ATTRIBUTES * sizeof(Datum)); + + /* Determine which attributes are assigned. */ + if (attributes & ROLE_ATTR_SUPERUSER) + temp_array[i++] = CStringGetTextDatum(_("Superuser")); + if (attributes & ROLE_ATTR_INHERIT) + temp_array[i++] = CStringGetTextDatum(_("Inherit")); + if (attributes & ROLE_ATTR_CREATEROLE) + temp_array[i++] = CStringGetTextDatum(_("Create Role")); + if (attributes & ROLE_ATTR_CREATEDB) + temp_array[i++] = CStringGetTextDatum(_("Create DB")); + if (attributes & ROLE_ATTR_CATUPDATE) + temp_array[i++] = CStringGetTextDatum(_("Catalog Update")); + if (attributes & ROLE_ATTR_CANLOGIN) + temp_array[i++] = CStringGetTextDatum(_("Login")); + if (attributes & ROLE_ATTR_REPLICATION) + temp_array[i++] = CStringGetTextDatum(_("Replication")); + if (attributes & ROLE_ATTR_BYPASSRLS) + temp_array[i++] = CStringGetTextDatum(_("Bypass RLS")); + + result = construct_array(temp_array, i, TEXTOID, -1, false, 'i'); + + PG_RETURN_ARRAYTYPE_P(result); +} + +/* + * convert_role_attr_string + * Convert text string to RoleAttr value. + */ +static RoleAttr +convert_role_attr_string(text *attr_type_text) +{ + char *attr_type = text_to_cstring(attr_type_text); + + if (pg_strcasecmp(attr_type, "SUPERUSER") == 0) + return ROLE_ATTR_SUPERUSER; + else if (pg_strcasecmp(attr_type, "INHERIT") == 0) + return ROLE_ATTR_INHERIT; + else if (pg_strcasecmp(attr_type, "CREATEROLE") == 0) + return ROLE_ATTR_CREATEROLE; + else if (pg_strcasecmp(attr_type, "CREATEDB") == 0) + return ROLE_ATTR_CREATEDB; + else if (pg_strcasecmp(attr_type, "CATUPDATE") == 0) + return ROLE_ATTR_CATUPDATE; + else if (pg_strcasecmp(attr_type, "CANLOGIN") == 0) + return ROLE_ATTR_CANLOGIN; + else if (pg_strcasecmp(attr_type, "REPLICATION") == 0) + return ROLE_ATTR_REPLICATION; + else if (pg_strcasecmp(attr_type, "BYPASSRLS") == 0) + return ROLE_ATTR_BYPASSRLS; + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized role attribute: \"%s\"", attr_type))); +} /* * initialization function (called by InitPostgres) @@ -4634,23 +4815,6 @@ RoleMembershipCacheCallback(Datum arg, int cacheid, uint32 hashvalue) } -/* Check if specified role has rolinherit set */ -static bool -has_rolinherit(Oid roleid) -{ - bool result = false; - HeapTuple utup; - - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolinherit; - ReleaseSysCache(utup); - } - return result; -} - - /* * Get a list of roles that the specified roleid has the privileges of * @@ -4697,7 +4861,7 @@ roles_has_privs_of(Oid roleid) int i; /* Ignore non-inheriting roles */ - if (!has_rolinherit(memberid)) + if (!has_role_attribute(memberid, ROLE_ATTR_INHERIT)) continue; /* Find roles that memberid is directly a member of */ diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 5c75390..ccb1066 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2308,7 +2308,7 @@ RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel) * bypassrls right or is the table owner of the table(s) involved which * have RLS enabled. */ - if (!has_bypassrls_privilege(GetUserId()) && + if (!have_role_attribute(ROLE_ATTR_BYPASSRLS) && ((pk_rel->rd_rel->relrowsecurity && !pg_class_ownercheck(pkrte->relid, GetUserId())) || (fk_rel->rd_rel->relrowsecurity && diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 8fccb4c..db2a0fb 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -40,6 +40,7 @@ #include "storage/pg_shmem.h" #include "storage/proc.h" #include "storage/procarray.h" +#include "utils/acl.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -329,24 +330,6 @@ SetUserIdAndContext(Oid userid, bool sec_def_context) /* - * Check whether specified role has explicit REPLICATION privilege - */ -bool -has_rolreplication(Oid roleid) -{ - bool result = false; - HeapTuple utup; - - utup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); - if (HeapTupleIsValid(utup)) - { - result = ((Form_pg_authid) GETSTRUCT(utup))->rolreplication; - ReleaseSysCache(utup); - } - return result; -} - -/* * Initialize user identity during normal backend startup */ void @@ -375,7 +358,7 @@ InitializeSessionUserId(const char *rolename) roleid = HeapTupleGetOid(roleTup); AuthenticatedUserId = roleid; - AuthenticatedUserIsSuperuser = rform->rolsuper; + AuthenticatedUserIsSuperuser = (rform->rolattr & ROLE_ATTR_SUPERUSER); /* This sets OuterUserId/CurrentUserId too */ SetSessionUserId(roleid, AuthenticatedUserIsSuperuser); @@ -394,7 +377,7 @@ InitializeSessionUserId(const char *rolename) /* * Is role allowed to login at all? */ - if (!rform->rolcanlogin) + if (!(rform->rolattr & ROLE_ATTR_CANLOGIN)) ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION), errmsg("role \"%s\" is not permitted to log in", diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index c348034..268001f 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -762,7 +762,7 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username, { Assert(!bootstrap); - if (!superuser() && !has_rolreplication(GetUserId())) + if (!have_role_attribute(ROLE_ATTR_REPLICATION)) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser or replication role to start walsender"))); diff --git a/src/backend/utils/misc/superuser.c b/src/backend/utils/misc/superuser.c index ff0f947..67d070c 100644 --- a/src/backend/utils/misc/superuser.c +++ b/src/backend/utils/misc/superuser.c @@ -58,6 +58,7 @@ superuser_arg(Oid roleid) { bool result; HeapTuple rtup; + RoleAttr attributes; /* Quick out for cache hit */ if (OidIsValid(last_roleid) && last_roleid == roleid) @@ -71,7 +72,8 @@ superuser_arg(Oid roleid) rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid)); if (HeapTupleIsValid(rtup)) { - result = ((Form_pg_authid) GETSTRUCT(rtup))->rolsuper; + attributes = ((Form_pg_authid) GETSTRUCT(rtup))->rolattr; + result = (attributes & ROLE_ATTR_SUPERUSER); ReleaseSysCache(rtup); } else diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index eb633bc..f638167 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -671,10 +671,16 @@ dumpRoles(PGconn *conn) /* note: rolconfig is dumped later */ if (server_version >= 90500) printfPQExpBuffer(buf, - "SELECT oid, rolname, rolsuper, rolinherit, " - "rolcreaterole, rolcreatedb, " - "rolcanlogin, rolconnlimit, rolpassword, " - "rolvaliduntil, rolreplication, rolbypassrls, " + "SELECT oid, rolname, " + "pg_check_role_attribute(oid, 'SUPERUSER') AS rolsuper, " + "pg_check_role_attribute(oid, 'INHERIT') AS rolinherit, " + "pg_check_role_attribute(oid, 'CREATEROLE') AS rolcreaterole, " + "pg_check_role_attribute(oid, 'CREATEDB') AS rolcreatedb, " + "pg_check_role_attribute(oid, 'CANLOGIN') AS rolcanlogin, " + "pg_check_role_attribute(oid, 'REPLICATION') AS rolreplication, " + "pg_check_role_attribute(oid, 'BYPASSRLS') AS rolbypassrls, " + "rolconnlimit, rolpassword, " + "rolvaliduntil, " "pg_catalog.shobj_description(oid, 'pg_authid') as rolcomment, " "rolname = current_user AS is_current_user " "FROM pg_authid " diff --git a/src/include/catalog/acldefs.h b/src/include/catalog/acldefs.h new file mode 100644 index 0000000..2dcc174 --- /dev/null +++ b/src/include/catalog/acldefs.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * acldefs.h + * base definitions for ACLs and role attributes + * + * Portions Copyright (c) 2014, PostgreSQL Global Development Group + * + * src/include/catalog/acldefs.h + * + *------------------------------------------------------------------------- + */ +#ifndef ACLDEFS_H +#define ACLDEFS_H + +/* + * Grantable rights are encoded so that we can OR them together in a bitmask. + * The present representation of AclItem limits us to 16 distinct rights, + * even though AclMode is defined as uint32. See utils/acl.h. + * + * Caution: changing these codes breaks stored ACLs, hence forces initdb. + */ +typedef uint32 AclMode; /* a bitmask of privilege bits */ + +#define ACL_INSERT (1<<0) /* for relations */ +#define ACL_SELECT (1<<1) +#define ACL_UPDATE (1<<2) +#define ACL_DELETE (1<<3) +#define ACL_TRUNCATE (1<<4) +#define ACL_REFERENCES (1<<5) +#define ACL_TRIGGER (1<<6) +#define ACL_EXECUTE (1<<7) /* for functions */ +#define ACL_USAGE (1<<8) /* for languages, namespaces, FDWs, and + * servers */ +#define ACL_CREATE (1<<9) /* for namespaces and databases */ +#define ACL_CREATE_TEMP (1<<10) /* for databases */ +#define ACL_CONNECT (1<<11) /* for databases */ +#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */ +#define ACL_NO_RIGHTS 0 +/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */ +#define ACL_SELECT_FOR_UPDATE ACL_UPDATE + +#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */ + + +/* + * Role attributes are encoded so that we can OR them together in a bitmask. + * The present representation of RoleAttr (defined in acl.h) limits us to 64 + * distinct rights. + * + * Note about ROLE_ATTR_ALL: This symbol is used verbatim by genbki.pl, which + * means we need to hard-code its value instead of using a symbolic definition. + * Therefore, whenever role attributes are changed, this value MUST be updated + * manually. + */ + +/* A bitmask for role attributes */ +typedef uint64 RoleAttr; + +#define ROLE_ATTR_NONE 0 +#define ROLE_ATTR_SUPERUSER (1<<0) +#define ROLE_ATTR_INHERIT (1<<1) +#define ROLE_ATTR_CREATEROLE (1<<2) +#define ROLE_ATTR_CREATEDB (1<<3) +#define ROLE_ATTR_CATUPDATE (1<<4) +#define ROLE_ATTR_CANLOGIN (1<<5) +#define ROLE_ATTR_REPLICATION (1<<6) +#define ROLE_ATTR_BYPASSRLS (1<<7) +#define N_ROLE_ATTRIBUTES 8 /* 1 plus the last 1<<x */ +#define ROLE_ATTR_ALL 255 /* (1 << N_ROLE_ATTRIBUTES) - 1 */ + + +#endif /* ACLDEFS_H */ diff --git a/src/include/catalog/pg_authid.h b/src/include/catalog/pg_authid.h index 3b63d2b..a45f38d 100644 --- a/src/include/catalog/pg_authid.h +++ b/src/include/catalog/pg_authid.h @@ -21,6 +21,7 @@ #ifndef PG_AUTHID_H #define PG_AUTHID_H +#include "catalog/acldefs.h" #include "catalog/genbki.h" /* @@ -45,16 +46,8 @@ CATALOG(pg_authid,1260) BKI_SHARED_RELATION BKI_ROWTYPE_OID(2842) BKI_SCHEMA_MACRO { NameData rolname; /* name of role */ - bool rolsuper; /* read this field via superuser() only! */ - bool rolinherit; /* inherit privileges from other roles? */ - bool rolcreaterole; /* allowed to create more roles? */ - bool rolcreatedb; /* allowed to create databases? */ - bool rolcatupdate; /* allowed to alter catalogs manually? */ - bool rolcanlogin; /* allowed to log in as session user? */ - bool rolreplication; /* role used for streaming replication */ - bool rolbypassrls; /* allowed to bypass row level security? */ + int64 rolattr; /* role attribute bitmask */ int32 rolconnlimit; /* max connections allowed (-1=no limit) */ - /* remaining fields may be null; use heap_getattr to read them! */ text rolpassword; /* password, if any */ timestamptz rolvaliduntil; /* password expiration time, if any */ @@ -74,28 +67,25 @@ typedef FormData_pg_authid *Form_pg_authid; * compiler constants for pg_authid * ---------------- */ -#define Natts_pg_authid 12 +#define Natts_pg_authid 5 #define Anum_pg_authid_rolname 1 -#define Anum_pg_authid_rolsuper 2 -#define Anum_pg_authid_rolinherit 3 -#define Anum_pg_authid_rolcreaterole 4 -#define Anum_pg_authid_rolcreatedb 5 -#define Anum_pg_authid_rolcatupdate 6 -#define Anum_pg_authid_rolcanlogin 7 -#define Anum_pg_authid_rolreplication 8 -#define Anum_pg_authid_rolbypassrls 9 -#define Anum_pg_authid_rolconnlimit 10 -#define Anum_pg_authid_rolpassword 11 -#define Anum_pg_authid_rolvaliduntil 12 +#define Anum_pg_authid_rolattr 2 +#define Anum_pg_authid_rolconnlimit 3 +#define Anum_pg_authid_rolpassword 4 +#define Anum_pg_authid_rolvaliduntil 5 + /* ---------------- * initial contents of pg_authid * * The uppercase quantities will be replaced at initdb time with * user choices. + * + * PGROLATTRALL is substituted by genbki.pl to use the value defined by + * ROLE_ATTR_ALL. * ---------------- */ -DATA(insert OID = 10 ( "POSTGRES" t t t t t t t t -1 _null_ _null_)); +DATA(insert OID = 10 ( "POSTGRES" PGROLATTRALL -1 _null_ _null_)); #define BOOTSTRAP_SUPERUSERID 10 diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index eace352..b4f93af 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5136,6 +5136,19 @@ DESCR("rank of hypothetical row without gaps"); DATA(insert OID = 3993 ( dense_rank_final PGNSP PGUID 12 1 0 2276 0 f f f f f f i 2 0 20 "2281 2276" "{2281,2276}" "{i,v}" _null_ _null_ hypothetical_dense_rank_final _null_ _null_ _null_ )); DESCR("aggregate final function"); +/* role attribute support functions */ +DATA(insert OID = 3994 ( pg_has_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_id _null_ _null_ _null_ )); +DESCR("check role attribute by role oid with superuser bypass check"); +DATA(insert OID = 3995 ( pg_has_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_has_role_attribute_name _null_ _null_ _null_ )); +DESCR("check role attribute by role name with superuser bypass check"); +DATA(insert OID = 3996 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_id _null_ _null_ _null_ )); +DESCR("check role attribute by role id"); +DATA(insert OID = 3997 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "19 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_name _null_ _null_ _null_ )); +DESCR("check role attribute by role name"); +DATA(insert OID = 3998 ( pg_check_role_attribute PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 16 "20 25" _null_ _null_ _null_ _null_ pg_check_role_attribute_attrs _null_ _null_ _null_ )); +DESCR("check role attribute"); +DATA(insert OID = 3999 ( pg_all_role_attributes PGNSP PGUID 12 10 0 0 0 f f f f t f s 1 0 1009 "20" _null_ _null_ _null_ _null_ pg_all_role_attributes _null_ _null_ _null_)); +DESCR("convert role attributes to string array"); /* * Symbolic values for provolatile column: these indicate whether the result diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 458eeb0..ecb5780 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -23,8 +23,10 @@ #include "nodes/bitmapset.h" #include "nodes/primnodes.h" #include "nodes/value.h" +#include "catalog/acldefs.h" #include "utils/lockwaitpolicy.h" + /* Possible sources of a Query */ typedef enum QuerySource { @@ -51,33 +53,6 @@ typedef enum SortByNulls SORTBY_NULLS_LAST } SortByNulls; -/* - * Grantable rights are encoded so that we can OR them together in a bitmask. - * The present representation of AclItem limits us to 16 distinct rights, - * even though AclMode is defined as uint32. See utils/acl.h. - * - * Caution: changing these codes breaks stored ACLs, hence forces initdb. - */ -typedef uint32 AclMode; /* a bitmask of privilege bits */ - -#define ACL_INSERT (1<<0) /* for relations */ -#define ACL_SELECT (1<<1) -#define ACL_UPDATE (1<<2) -#define ACL_DELETE (1<<3) -#define ACL_TRUNCATE (1<<4) -#define ACL_REFERENCES (1<<5) -#define ACL_TRIGGER (1<<6) -#define ACL_EXECUTE (1<<7) /* for functions */ -#define ACL_USAGE (1<<8) /* for languages, namespaces, FDWs, and - * servers */ -#define ACL_CREATE (1<<9) /* for namespaces and databases */ -#define ACL_CREATE_TEMP (1<<10) /* for databases */ -#define ACL_CONNECT (1<<11) /* for databases */ -#define N_ACL_RIGHTS 12 /* 1 plus the last 1<<x */ -#define ACL_NO_RIGHTS 0 -/* Currently, SELECT ... FOR [KEY] UPDATE/SHARE requires UPDATE privileges */ -#define ACL_SELECT_FOR_UPDATE ACL_UPDATE - /***************************************************************************** * Query Tree diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index a8e3164..4e8d81c 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -30,13 +30,6 @@ /* - * typedef AclMode is declared in parsenodes.h, also the individual privilege - * bit meanings are defined there - */ - -#define ACL_ID_PUBLIC 0 /* placeholder for id in a PUBLIC acl item */ - -/* * AclItem * * Note: must be same size on all platforms, because the size is hardcoded @@ -326,7 +319,10 @@ extern bool pg_foreign_data_wrapper_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_foreign_server_ownercheck(Oid srv_oid, Oid roleid); extern bool pg_event_trigger_ownercheck(Oid et_oid, Oid roleid); extern bool pg_extension_ownercheck(Oid ext_oid, Oid roleid); -extern bool has_createrole_privilege(Oid roleid); -extern bool has_bypassrls_privilege(Oid roleid); + +/* role attribute check routines */ +extern bool has_role_attribute(Oid roleid, RoleAttr attribute); +extern bool have_role_attribute(RoleAttr attribute); +extern bool check_role_attribute(Oid roleid, RoleAttr attribute); #endif /* ACL_H */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 2da3002..c8e0e3a 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -106,6 +106,12 @@ extern Datum pg_has_role_id_name(PG_FUNCTION_ARGS); extern Datum pg_has_role_id_id(PG_FUNCTION_ARGS); extern Datum pg_has_role_name(PG_FUNCTION_ARGS); extern Datum pg_has_role_id(PG_FUNCTION_ARGS); +extern Datum pg_has_role_attribute_id(PG_FUNCTION_ARGS); +extern Datum pg_has_role_attribute_name(PG_FUNCTION_ARGS); +extern Datum pg_check_role_attribute_id(PG_FUNCTION_ARGS); +extern Datum pg_check_role_attribute_name(PG_FUNCTION_ARGS); +extern Datum pg_check_role_attribute_attrs(PG_FUNCTION_ARGS); +extern Datum pg_all_role_attributes(PG_FUNCTION_ARGS); /* bool.c */ extern Datum boolin(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 80c3351..d105215 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1314,7 +1314,7 @@ pg_group| SELECT pg_authid.rolname AS groname, FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid - WHERE (NOT pg_authid.rolcanlogin); + WHERE (NOT pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text)); pg_indexes| SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, @@ -1405,17 +1405,17 @@ pg_replication_slots| SELECT l.slot_name, FROM (pg_get_replication_slots() l(slot_name, plugin, slot_type, datoid, active, xmin, catalog_xmin, restart_lsn) LEFT JOIN pg_database d ON ((l.datoid = d.oid))); pg_roles| SELECT pg_authid.rolname, - pg_authid.rolsuper, - pg_authid.rolinherit, - pg_authid.rolcreaterole, - pg_authid.rolcreatedb, - pg_authid.rolcatupdate, - pg_authid.rolcanlogin, - pg_authid.rolreplication, + pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS rolsuper, + pg_check_role_attribute(pg_authid.rolattr, 'INHERIT'::text) AS rolinherit, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEROLE'::text) AS rolcreaterole, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS rolcreatedb, + pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS rolcatupdate, + pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text) AS rolcanlogin, + pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS rolreplication, + pg_check_role_attribute(pg_authid.rolattr, 'BYPASSRLS'::text) AS rolbypassrls, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, - pg_authid.rolbypassrls, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid @@ -1608,16 +1608,16 @@ pg_settings| SELECT a.name, FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); pg_shadow| SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, - pg_authid.rolcreatedb AS usecreatedb, - pg_authid.rolsuper AS usesuper, - pg_authid.rolcatupdate AS usecatupd, - pg_authid.rolreplication AS userepl, + pg_check_role_attribute(pg_authid.rolattr, 'CREATEDB'::text) AS usecreatedb, + pg_check_role_attribute(pg_authid.rolattr, 'SUPERUSER'::text) AS usesuper, + pg_check_role_attribute(pg_authid.rolattr, 'CATUPDATE'::text) AS usecatupd, + pg_check_role_attribute(pg_authid.rolattr, 'REPLICATION'::text) AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) - WHERE pg_authid.rolcanlogin; + WHERE pg_check_role_attribute(pg_authid.rolattr, 'CANLOGIN'::text); pg_stat_activity| SELECT s.datid, d.datname, s.pid,
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers