From be6bca23b34b6e59f1deed1d6b44bb434dbe890e Mon Sep 17 00:00:00 2001 From: Joe Conway Date: Mon, 3 Aug 2009 21:11:40 +0000 Subject: [PATCH] Implement has_sequence_privilege() Add family of functions that did not exist earlier, mainly due to historical omission. Original patch by Abhijit Menon-Sen, with review and modifications by Joe Conway. catversion.h bumped. --- doc/src/sgml/func.sgml | 30 +++- src/backend/utils/adt/acl.c | 214 ++++++++++++++++++++++- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_proc.h | 15 +- src/include/utils/builtins.h | 8 +- src/test/regress/expected/privileges.out | 22 +++ src/test/regress/sql/privileges.sql | 17 ++ 7 files changed, 304 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index b84f7d18dd..4e5b08d8e4 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -11788,6 +11788,21 @@ SET search_path TO schema , schema, .. boolean does current user have privilege for foreign server + + has_sequence_privilege(user, + sequence, + privilege) + + boolean + does user have privilege for sequence + + + has_sequence_privilege(sequence, + privilege) + + boolean + does current user have privilege for sequence + has_table_privilege(user, table, @@ -11861,6 +11876,9 @@ SET search_path TO schema , schema, .. has_server_privilege + + has_sequence_privilege + has_table_privilege @@ -11900,6 +11918,16 @@ SELECT has_table_privilege('joe', 'mytable', 'INSERT, SELECT WITH GRANT OPTION') + + has_sequence_privilege checks whether a user + can access a sequence in a particular way. The possibilities for its + arguments are analogous to has_table_privilege. + The desired access privilege type must evaluate to one of + USAGE, + SELECT, or + UPDATE. + + has_any_column_privilege checks whether a user can access any column of a table in a particular way. diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c index 67c4ea36e3..83d93ad348 100644 --- a/src/backend/utils/adt/acl.c +++ b/src/backend/utils/adt/acl.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.148 2009/06/11 14:49:03 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/acl.c,v 1.149 2009/08/03 21:11:39 joe Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "catalog/pg_authid.h" #include "catalog/pg_auth_members.h" #include "catalog/pg_type.h" +#include "catalog/pg_class.h" #include "commands/dbcommands.h" #include "commands/tablespace.h" #include "foreign/foreign.h" @@ -88,6 +89,7 @@ static AclMode convert_any_priv_string(text *priv_type_text, static Oid convert_table_name(text *tablename); static AclMode convert_table_priv_string(text *priv_type_text); +static AclMode convert_sequence_priv_string(text *priv_type_text); static AttrNumber convert_column_name(Oid tableoid, text *column); static AclMode convert_column_priv_string(text *priv_type_text); static Oid convert_database_name(text *databasename); @@ -1704,6 +1706,216 @@ convert_table_priv_string(text *priv_type_text) return convert_any_priv_string(priv_type_text, table_priv_map); } +/* + * has_sequence_privilege variants + * These are all named "has_sequence_privilege" at the SQL level. + * They take various combinations of relation name, relation OID, + * user name, user OID, or implicit user = current_user. + * + * The result is a boolean value: true if user has the indicated + * privilege, false if not. The variants that take a relation OID + * return NULL if the OID doesn't exist. + */ + +/* + * has_sequence_privilege_name_name + * Check user privileges on a sequence given + * name username, text sequencename, and text priv name. + */ +Datum +has_sequence_privilege_name_name(PG_FUNCTION_ARGS) +{ + Name rolename = PG_GETARG_NAME(0); + text *sequencename = PG_GETARG_TEXT_P(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid roleid; + Oid sequenceoid; + AclMode mode; + AclResult aclresult; + + roleid = get_roleid_checked(NameStr(*rolename)); + mode = convert_sequence_priv_string(priv_type_text); + sequenceoid = convert_table_name(sequencename); + if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + text_to_cstring(sequencename)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_sequence_privilege_name + * Check user privileges on a sequence given + * text sequencename and text priv name. + * current_user is assumed + */ +Datum +has_sequence_privilege_name(PG_FUNCTION_ARGS) +{ + text *sequencename = PG_GETARG_TEXT_P(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + Oid sequenceoid; + AclMode mode; + AclResult aclresult; + + roleid = GetUserId(); + mode = convert_sequence_priv_string(priv_type_text); + sequenceoid = convert_table_name(sequencename); + if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + text_to_cstring(sequencename)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_sequence_privilege_name_id + * Check user privileges on a sequence given + * name usename, sequence oid, and text priv name. + */ +Datum +has_sequence_privilege_name_id(PG_FUNCTION_ARGS) +{ + Name username = PG_GETARG_NAME(0); + Oid sequenceoid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid roleid; + AclMode mode; + AclResult aclresult; + char relkind; + + roleid = get_roleid_checked(NameStr(*username)); + mode = convert_sequence_priv_string(priv_type_text); + relkind = get_rel_relkind(sequenceoid); + if (relkind == '\0') + PG_RETURN_NULL(); + else if (relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + get_rel_name(sequenceoid)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_sequence_privilege_id + * Check user privileges on a sequence given + * sequence oid, and text priv name. + * current_user is assumed + */ +Datum +has_sequence_privilege_id(PG_FUNCTION_ARGS) +{ + Oid sequenceoid = PG_GETARG_OID(0); + text *priv_type_text = PG_GETARG_TEXT_P(1); + Oid roleid; + AclMode mode; + AclResult aclresult; + char relkind; + + roleid = GetUserId(); + mode = convert_sequence_priv_string(priv_type_text); + relkind = get_rel_relkind(sequenceoid); + if (relkind == '\0') + PG_RETURN_NULL(); + else if (relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + get_rel_name(sequenceoid)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_sequence_privilege_id_name + * Check user privileges on a sequence given + * roleid, text sequencename, and text priv name. + */ +Datum +has_sequence_privilege_id_name(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + text *sequencename = PG_GETARG_TEXT_P(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + Oid sequenceoid; + AclMode mode; + AclResult aclresult; + + mode = convert_sequence_priv_string(priv_type_text); + sequenceoid = convert_table_name(sequencename); + if (get_rel_relkind(sequenceoid) != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + text_to_cstring(sequencename)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * has_sequence_privilege_id_id + * Check user privileges on a sequence given + * roleid, sequence oid, and text priv name. + */ +Datum +has_sequence_privilege_id_id(PG_FUNCTION_ARGS) +{ + Oid roleid = PG_GETARG_OID(0); + Oid sequenceoid = PG_GETARG_OID(1); + text *priv_type_text = PG_GETARG_TEXT_P(2); + AclMode mode; + AclResult aclresult; + char relkind; + + mode = convert_sequence_priv_string(priv_type_text); + relkind = get_rel_relkind(sequenceoid); + if (relkind == '\0') + PG_RETURN_NULL(); + else if (relkind != RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is not a sequence", + get_rel_name(sequenceoid)))); + + aclresult = pg_class_aclcheck(sequenceoid, roleid, mode); + + PG_RETURN_BOOL(aclresult == ACLCHECK_OK); +} + +/* + * convert_sequence_priv_string + * Convert text string to AclMode value. + */ +static AclMode +convert_sequence_priv_string(text *priv_type_text) +{ + static const priv_map sequence_priv_map[] = { + { "USAGE", ACL_USAGE }, + { "SELECT", ACL_SELECT }, + { "UPDATE", ACL_UPDATE }, + { NULL, 0 } + }; + + return convert_any_priv_string(priv_type_text, sequence_priv_map); +} + /* * has_any_column_privilege variants diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index dc1b41d06b..c1b8f6b6a8 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.535 2009/08/02 22:14:52 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.536 2009/08/03 21:11:39 joe Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200908021 +#define CATALOG_VERSION_NO 200908031 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index d5884faa4a..89effab149 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.547 2009/07/29 20:56:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.548 2009/08/03 21:11:39 joe Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -2918,6 +2918,19 @@ DESCR("current user privilege on relation by rel name"); DATA(insert OID = 1927 ( has_table_privilege PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_table_privilege_id _null_ _null_ _null_ )); DESCR("current user privilege on relation by rel oid"); +DATA(insert OID = 2181 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 3 0 16 "19 25 25" _null_ _null_ _null_ _null_ has_sequence_privilege_name_name _null_ _null_ _null_ )); +DESCR("user privilege on sequence by username, seq name"); +DATA(insert OID = 2182 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 3 0 16 "19 26 25" _null_ _null_ _null_ _null_ has_sequence_privilege_name_id _null_ _null_ _null_ )); +DESCR("user privilege on sequence by username, seq oid"); +DATA(insert OID = 2183 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 3 0 16 "26 25 25" _null_ _null_ _null_ _null_ has_sequence_privilege_id_name _null_ _null_ _null_ )); +DESCR("user privilege on sequence by user oid, seq name"); +DATA(insert OID = 2184 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 3 0 16 "26 26 25" _null_ _null_ _null_ _null_ has_sequence_privilege_id_id _null_ _null_ _null_ )); +DESCR("user privilege on sequence by user oid, seq oid"); +DATA(insert OID = 2185 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "25 25" _null_ _null_ _null_ _null_ has_sequence_privilege_name _null_ _null_ _null_ )); +DESCR("current user privilege on sequence by seq name"); +DATA(insert OID = 2186 ( has_sequence_privilege PGNSP PGUID 12 1 0 0 f f f t f s 2 0 16 "26 25" _null_ _null_ _null_ _null_ has_sequence_privilege_id _null_ _null_ _null_ )); +DESCR("current user privilege on sequence by seq oid"); + DATA(insert OID = 3012 ( has_column_privilege PGNSP PGUID 12 1 0 0 f f f t f s 4 0 16 "19 25 25 25" _null_ _null_ _null_ _null_ has_column_privilege_name_name_name _null_ _null_ _null_ )); DESCR("user privilege on column by username, rel name, col name"); DATA(insert OID = 3013 ( has_column_privilege PGNSP PGUID 12 1 0 0 f f f t f s 4 0 16 "19 25 21 25" _null_ _null_ _null_ _null_ has_column_privilege_name_name_attnum _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 481eacf9b9..4b92cbcb60 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.336 2009/08/01 19:59:41 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.337 2009/08/03 21:11:39 joe Exp $ * *------------------------------------------------------------------------- */ @@ -46,6 +46,12 @@ extern Datum has_table_privilege_id_name(PG_FUNCTION_ARGS); extern Datum has_table_privilege_id_id(PG_FUNCTION_ARGS); extern Datum has_table_privilege_name(PG_FUNCTION_ARGS); extern Datum has_table_privilege_id(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_name_name(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_name_id(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_id_name(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_id_id(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_name(PG_FUNCTION_ARGS); +extern Datum has_sequence_privilege_id(PG_FUNCTION_ARGS); extern Datum has_database_privilege_name_name(PG_FUNCTION_ARGS); extern Datum has_database_privilege_name_id(PG_FUNCTION_ARGS); extern Datum has_database_privilege_id_name(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/privileges.out b/src/test/regress/expected/privileges.out index a17ff59d0c..809b656217 100644 --- a/src/test/regress/expected/privileges.out +++ b/src/test/regress/expected/privileges.out @@ -815,8 +815,30 @@ SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION') t (1 row) +-- has_sequence_privilege tests +\c - +CREATE SEQUENCE x_seq; +GRANT USAGE on x_seq to regressuser2; +SELECT has_sequence_privilege('regressuser1', 'atest1', 'SELECT'); +ERROR: "atest1" is not a sequence +SELECT has_sequence_privilege('regressuser1', 'x_seq', 'INSERT'); +ERROR: unrecognized privilege type: "INSERT" +SELECT has_sequence_privilege('regressuser1', 'x_seq', 'SELECT'); + has_sequence_privilege +------------------------ + f +(1 row) + +SET SESSION AUTHORIZATION regressuser2; +SELECT has_sequence_privilege('x_seq', 'USAGE'); + has_sequence_privilege +------------------------ + t +(1 row) + -- clean up \c +drop sequence x_seq; DROP FUNCTION testfunc2(int); DROP FUNCTION testfunc4(boolean); DROP VIEW atestv1; diff --git a/src/test/regress/sql/privileges.sql b/src/test/regress/sql/privileges.sql index 5aa1012f3f..917e8e5da4 100644 --- a/src/test/regress/sql/privileges.sql +++ b/src/test/regress/sql/privileges.sql @@ -469,10 +469,27 @@ SELECT has_table_privilege('regressuser3', 'atest4', 'SELECT'); -- false SELECT has_table_privilege('regressuser1', 'atest4', 'SELECT WITH GRANT OPTION'); -- true +-- has_sequence_privilege tests +\c - + +CREATE SEQUENCE x_seq; + +GRANT USAGE on x_seq to regressuser2; + +SELECT has_sequence_privilege('regressuser1', 'atest1', 'SELECT'); +SELECT has_sequence_privilege('regressuser1', 'x_seq', 'INSERT'); +SELECT has_sequence_privilege('regressuser1', 'x_seq', 'SELECT'); + +SET SESSION AUTHORIZATION regressuser2; + +SELECT has_sequence_privilege('x_seq', 'USAGE'); + -- clean up \c +drop sequence x_seq; + DROP FUNCTION testfunc2(int); DROP FUNCTION testfunc4(boolean);