It is true that you cannot grant EXEC
permissions on a function that returns a table. This type of function is effectively more of a view than a function. You need to grant SELECT instead, e.g.:
GRANT SELECT ON dbo.Table_Valued_Function TO [testuser];
So your script would look more like this (sorry, but I absolutely loathe INFORMATION_SCHEMA
and much prefer to use the catalog views, which also don't need functions like OBJECTPROPERTY
):
DECLARE
@sql NVARCHAR(MAX) = N'',
@username VARCHAR(255) = 'testuser';
SELECT @sql += CHAR(13) + CHAR(10) + N'GRANT ' + CASE
WHEN type_desc LIKE 'SQL_%TABLE_VALUED_FUNCTION'
OR type_desc = 'VIEW'
THEN ' SELECT ' ELSE ' EXEC ' END
+ ' ON ' + QUOTENAME(SCHEMA_NAME([schema_id]))
+ '.' + QUOTENAME(name)
+ ' TO ' + @username + ';'
FROM sys.all_objects
WHERE is_ms_shipped = 0 AND
(
type_desc LIKE '%PROCEDURE'
OR type_desc LIKE '%FUNCTION'
OR type_desc = 'VIEW'
);
PRINT @sql;
-- EXEC sp_executesql @sql;
Now you can grant EXEC
on a schema, and always create these procedures in that schema (actually one of the purposes of schemas!), which @jgardner04 already suggested, however in order for this solution to apply to table-valued functions as well, you'd also have to grant SELECT
. Which is okay if you're not storing any data in tables in that schema (or at least that you want to hide from them), but it will apply to any tables and views as well, which might not be your intention.
Another idea (e.g. if you can't, or don't want to, use schemas) is to write a DDL Trigger that captures the CREATE_PROCEDURE
, CREATE_FUNCTION
and CREATE_VIEW
events, and grants permissions to a user (or a set of users, if you want to store them in a table):
CREATE TRIGGER ApplyPermissionsToAllProceduressAndFunctions -- be more creative!
ON DATABASE FOR CREATE_PROCEDURE, CREATE_FUNCTION, CREATE_VIEW
AS
BEGIN
SET NOCOUNT ON;
DECLARE
@sql NVARCHAR(MAX),
@EventData XML = EVENTDATA();
;WITH x ( sch, obj )
AS
(
SELECT
@EventData.value('(/EVENT_INSTANCE/SchemaName)[1]', 'NVARCHAR(255)'),
@EventData.value('(/EVENT_INSTANCE/ObjectName)[1]', 'NVARCHAR(255)')
)
SELECT @sql = N'GRANT ' + CASE
WHEN o.type_desc LIKE 'SQL_%TABLE_VALUED_FUNCTION'
OR o.type_desc = 'VIEW'
THEN ' SELECT '
ELSE ' EXEC ' END
+ ' ON ' + QUOTENAME(x.sch)
+ '.' + QUOTENAME(x.obj)
+ ' TO testuser;' -- hard-code this, use a variable, or store in a table
FROM x
INNER JOIN sys.objects AS o
ON o.[object_id] = OBJECT_ID(QUOTENAME(x.sch) + '.' + QUOTENAME(x.obj));
EXEC sp_executesql @sql;
END
GO
The drawback I find with DDL Triggers is that you can quickly forget that they're there. So a year down the road when you decide to stop granting these permissions to all new objects, it might take a while to troubleshoot why it's still happening. At my last job we logged all actions invoked by DDL triggers to a central "event log" of sorts, and that was our go-to place for tracking down any actions that happened on the server that nobody seems to remember (and it was a DDL trigger about half the time). So you may consider adding some additional logic that will help with that.
EDIT
Adding code for schema-based, and I'll mention again that this will grant permissions on any procedures, functions and tables created in the foo schema.
CREATE SCHEMA foo;
GRANT EXEC, SELECT ON SCHEMA::foo TO testuser;
Now if you create the following procedure, testuser will be able to execute:
CREATE PROCEDURE foo.proc1
AS
BEGIN
SET NOCOUNT ON;
SELECT 1;
END
GO
Trigger functions behave just like other functions as far as privileges are concerned. With a minor exception:
To create a trigger on a table, the user must have the TRIGGER
privilege on the table. The user must also have EXECUTE
privilege on the trigger function.
UPDATE
After feedback in the comments I did some research. There is an open TODO item in the Postgres Wiki:
Tighten trigger permission checks
Linked to this thread on Postgres hackers. Currently, EXECUTE
privileges on a trigger function are only checked at trigger create time, but not at runtime. So revoking EXECUTE on the trigger function has no effect on a trigger once created. Your observation seems to be correct.
This does not grant any additional privileges to manipulate objects. If the calling role lacks privileges needed to execute (parts of) the function body, the usual exception is raised. To pave the way, you could make a privileged user OWNER
of the function and use the
SECURITY DEFINER
clause, as documented in the manual here. It causes the function to be run with the permissions of the owner instead of the invoker (default).
If the owner is a superuser, you need to be extra careful who you grant the EXECUTE
privilege and what the function can do to avoid abuse. You may want to
REVOKE ALL ON FUNCTION foo() FROM public;
to begin with and use SET search_path
for the function.
Be sure to read the chapter on Writing SECURITY DEFINER
Functions Safely.
Find a code example in this related answer on SO.
Best Answer
The only permission applicable to functions is EXECUTE, so when you use ALL postgresql will try to apply all the permissions available for functions, and for now this is just "EXECUTE".
So it means exactly the same.
https://www.postgresql.org/docs/10/static/sql-grant.html
But be carefully if more kind of permissions for functions be implemented in new postgresql versions this can do wrong (but i can't see other than execute).