Add has_largeobject_privilege function.

This function checks whether a user has specific privileges on a large object,
identified by OID. The user can be provided by name, OID,
or default to the current user. If the specified large object doesn't exist,
the function returns NULL. It raises an error for a non-existent user name.
This behavior is basically consistent with other privilege inquiry functions
like has_table_privilege.

Bump catalog version.

Author: Yugo Nagata
Reviewed-by: Fujii Masao
Discussion: https://postgr.es/m/20240702163444.ab586f6075e502eb84f11b1a@sranhm.sraoss.co.jp
This commit is contained in:
Fujii Masao 2024-09-12 21:48:58 +09:00
parent 412229d197
commit 4eada203a5
6 changed files with 376 additions and 1 deletions

View File

@ -25117,6 +25117,24 @@ SELECT has_function_privilege('joeuser', 'myfunc(int, text)', 'execute');
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>has_largeobject_privilege</primary>
</indexterm>
<function>has_largeobject_privilege</function> (
<optional> <parameter>user</parameter> <type>name</type> or <type>oid</type>, </optional>
<parameter>largeobject</parameter> <type>oid</type>,
<parameter>privilege</parameter> <type>text</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Does user have privilege for large object?
Allowable privilege types are
<literal>SELECT</literal> and <literal>UPDATE</literal>.
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>

View File

@ -26,6 +26,7 @@
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_tablespace.h"
@ -39,6 +40,7 @@
#include "lib/bloomfilter.h"
#include "lib/qunique.h"
#include "miscadmin.h"
#include "storage/large_object.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
@ -46,6 +48,7 @@
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
@ -124,6 +127,7 @@ static AclMode convert_tablespace_priv_string(text *priv_type_text);
static Oid convert_type_name(text *typename);
static AclMode convert_type_priv_string(text *priv_type_text);
static AclMode convert_parameter_priv_string(text *priv_text);
static AclMode convert_largeobject_priv_string(text *priv_text);
static AclMode convert_role_priv_string(text *priv_type_text);
static AclResult pg_role_aclcheck(Oid role_oid, Oid roleid, AclMode mode);
@ -4663,6 +4667,142 @@ convert_parameter_priv_string(text *priv_text)
return convert_any_priv_string(priv_text, parameter_priv_map);
}
/*
* has_largeobject_privilege variants
* These are all named "has_largeobject_privilege" at the SQL level.
* They take various combinations of large object OID with
* 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, or NULL if object doesn't exist.
*/
/*
* has_lo_priv_byid
*
* Helper function to check user privileges on a large object given the
* role by Oid, large object by Oid, and privileges as AclMode.
*/
static bool
has_lo_priv_byid(Oid roleid, Oid lobjId, AclMode priv, bool *is_missing)
{
Snapshot snapshot = NULL;
AclResult aclresult;
if (priv & ACL_UPDATE)
snapshot = NULL;
else
snapshot = GetActiveSnapshot();
if (!LargeObjectExistsWithSnapshot(lobjId, snapshot))
{
Assert(is_missing != NULL);
*is_missing = true;
return false;
}
if (lo_compat_privileges)
return true;
aclresult = pg_largeobject_aclcheck_snapshot(lobjId,
roleid,
priv,
snapshot);
return aclresult == ACLCHECK_OK;
}
/*
* has_largeobject_privilege_name_id
* Check user privileges on a large object given
* name username, large object oid, and text priv name.
*/
Datum
has_largeobject_privilege_name_id(PG_FUNCTION_ARGS)
{
Name username = PG_GETARG_NAME(0);
Oid roleid = get_role_oid_or_public(NameStr(*username));
Oid lobjId = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* has_largeobject_privilege_id
* Check user privileges on a large object given
* large object oid, and text priv name.
* current_user is assumed
*/
Datum
has_largeobject_privilege_id(PG_FUNCTION_ARGS)
{
Oid lobjId = PG_GETARG_OID(0);
Oid roleid = GetUserId();
text *priv_type_text = PG_GETARG_TEXT_PP(1);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* has_largeobject_privilege_id_id
* Check user privileges on a large object given
* roleid, large object oid, and text priv name.
*/
Datum
has_largeobject_privilege_id_id(PG_FUNCTION_ARGS)
{
Oid roleid = PG_GETARG_OID(0);
Oid lobjId = PG_GETARG_OID(1);
text *priv_type_text = PG_GETARG_TEXT_PP(2);
AclMode mode;
bool is_missing = false;
bool result;
mode = convert_largeobject_priv_string(priv_type_text);
result = has_lo_priv_byid(roleid, lobjId, mode, &is_missing);
if (is_missing)
PG_RETURN_NULL();
PG_RETURN_BOOL(result);
}
/*
* convert_largeobject_priv_string
* Convert text string to AclMode value.
*/
static AclMode
convert_largeobject_priv_string(text *priv_type_text)
{
static const priv_map largeobject_priv_map[] = {
{"SELECT", ACL_SELECT},
{"SELECT WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_SELECT)},
{"UPDATE", ACL_UPDATE},
{"UPDATE WITH GRANT OPTION", ACL_GRANT_OPTION_FOR(ACL_UPDATE)},
{NULL, 0}
};
return convert_any_priv_string(priv_type_text, largeobject_priv_map);
}
/*
* pg_has_role variants
* These are all named "pg_has_role" at the SQL level.

View File

@ -57,6 +57,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202409121
#define CATALOG_VERSION_NO 202409122
#endif

View File

@ -5369,6 +5369,19 @@
prorettype => 'bool', proargtypes => 'oid text',
prosrc => 'has_any_column_privilege_id' },
{ oid => '4551', descr => 'user privilege on large objct by username, large object oid',
proname => 'has_largeobject_privilege', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'name oid text',
prosrc => 'has_largeobject_privilege_name_id' },
{ oid => '4552', descr => 'current privilege on large objct by large object oid',
proname => 'has_largeobject_privilege', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid text',
prosrc => 'has_largeobject_privilege_id' },
{ oid => '4553', descr => 'user privilege on large objct by user oid, large object oid',
proname => 'has_largeobject_privilege', procost => '10', provolatile => 's',
prorettype => 'bool', proargtypes => 'oid oid text',
prosrc => 'has_largeobject_privilege_id_id' },
{ oid => '3355', descr => 'I/O',
proname => 'pg_ndistinct_in', prorettype => 'pg_ndistinct',
proargtypes => 'cstring', prosrc => 'pg_ndistinct_in' },

View File

@ -2068,10 +2068,160 @@ SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
0
(1 row)
-- has_largeobject_privilege function
-- superuser
\c -
SELECT has_largeobject_privilege(1001, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1002, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1003, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1004, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1001, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1002, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1003, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1004, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
-- not-existing large object
SELECT has_largeobject_privilege(9999, 'SELECT'); -- NULL
has_largeobject_privilege
---------------------------
(1 row)
-- non-superuser
SET SESSION AUTHORIZATION regress_priv_user2;
SELECT has_largeobject_privilege(1001, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1002, 'SELECT'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege(1003, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1004, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1001, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege(1003, 'UPDATE'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege(1004, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege('regress_priv_user3', 1001, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege('regress_priv_user3', 1003, 'SELECT'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege('regress_priv_user3', 1005, 'SELECT');
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege('regress_priv_user3', 1005, 'UPDATE'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege('regress_priv_user3', 2001, 'UPDATE');
has_largeobject_privilege
---------------------------
t
(1 row)
-- compatibility mode in largeobject permission
\c -
SET lo_compat_privileges = false; -- default setting
SET SESSION AUTHORIZATION regress_priv_user4;
SELECT has_largeobject_privilege(1002, 'SELECT'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- false
has_largeobject_privilege
---------------------------
f
(1 row)
SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
ERROR: permission denied for large object 1002
SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
@ -2091,6 +2241,18 @@ ERROR: permission denied for function lo_import
\c -
SET lo_compat_privileges = true; -- compatibility mode
SET SESSION AUTHORIZATION regress_priv_user4;
SELECT has_largeobject_privilege(1002, 'SELECT'); -- true
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- true
has_largeobject_privilege
---------------------------
t
(1 row)
SELECT loread(lo_open(1002, x'40000'::int), 32);
loread
--------

View File

@ -1324,11 +1324,50 @@ SELECT loread(lo_open(1005, x'40000'::int), 32);
SELECT lo_truncate(lo_open(1005, x'20000'::int), 10); -- to be denied
SELECT lo_truncate(lo_open(2001, x'20000'::int), 10);
-- has_largeobject_privilege function
-- superuser
\c -
SELECT has_largeobject_privilege(1001, 'SELECT');
SELECT has_largeobject_privilege(1002, 'SELECT');
SELECT has_largeobject_privilege(1003, 'SELECT');
SELECT has_largeobject_privilege(1004, 'SELECT');
SELECT has_largeobject_privilege(1001, 'UPDATE');
SELECT has_largeobject_privilege(1002, 'UPDATE');
SELECT has_largeobject_privilege(1003, 'UPDATE');
SELECT has_largeobject_privilege(1004, 'UPDATE');
-- not-existing large object
SELECT has_largeobject_privilege(9999, 'SELECT'); -- NULL
-- non-superuser
SET SESSION AUTHORIZATION regress_priv_user2;
SELECT has_largeobject_privilege(1001, 'SELECT');
SELECT has_largeobject_privilege(1002, 'SELECT'); -- false
SELECT has_largeobject_privilege(1003, 'SELECT');
SELECT has_largeobject_privilege(1004, 'SELECT');
SELECT has_largeobject_privilege(1001, 'UPDATE');
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- false
SELECT has_largeobject_privilege(1003, 'UPDATE'); -- false
SELECT has_largeobject_privilege(1004, 'UPDATE');
SELECT has_largeobject_privilege('regress_priv_user3', 1001, 'SELECT');
SELECT has_largeobject_privilege('regress_priv_user3', 1003, 'SELECT'); -- false
SELECT has_largeobject_privilege('regress_priv_user3', 1005, 'SELECT');
SELECT has_largeobject_privilege('regress_priv_user3', 1005, 'UPDATE'); -- false
SELECT has_largeobject_privilege('regress_priv_user3', 2001, 'UPDATE');
-- compatibility mode in largeobject permission
\c -
SET lo_compat_privileges = false; -- default setting
SET SESSION AUTHORIZATION regress_priv_user4;
SELECT has_largeobject_privilege(1002, 'SELECT'); -- false
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- false
SELECT loread(lo_open(1002, x'40000'::int), 32); -- to be denied
SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd'); -- to be denied
SELECT lo_truncate(lo_open(1002, x'20000'::int), 10); -- to be denied
@ -1342,6 +1381,9 @@ SELECT lo_import('/dev/null', 2003); -- to be denied
SET lo_compat_privileges = true; -- compatibility mode
SET SESSION AUTHORIZATION regress_priv_user4;
SELECT has_largeobject_privilege(1002, 'SELECT'); -- true
SELECT has_largeobject_privilege(1002, 'UPDATE'); -- true
SELECT loread(lo_open(1002, x'40000'::int), 32);
SELECT lowrite(lo_open(1002, x'20000'::int), 'abcd');
SELECT lo_truncate(lo_open(1002, x'20000'::int), 10);