Snowflake task is returning a usage error that must be granted to owner role - scheduled-tasks

I am having a test procedure owned by prod_admin:
CREATE OR REPLACE PROCEDURE test()
RETURNS VARIANT
LANGUAGE JAVASCRIPT
EXECUTE AS OWNER
AS
$$
query = "SELECT * FROM DWH_OPS.CHANGE_HISTORY";
stmt = snowflake.createStatement({sqlText: query}).execute();
stmt.next();
return stmt;
$$;
And a task to run this procedure and owned by task_admin:
CREATE OR REPLACE TASK test_procedure
WAREHOUSE = 'COMPUTE_WH'
TIMESTAMP_INPUT_FORMAT = 'YYYY-MM-DD HH24'
SCHEDULE = '5 MINUTE'
AS
call PROD_DB.DWH_OPS.TEST();
Once the task is running, we are receiving the following error:
USAGE privilege on the task's warehouse must be granted to owner role
Both roles are having usage privileges over the same warehouse. We've ran show grants to both roles, and they do have access.
When running the procedure using CALL test(), it worked using both roles.

There was 2 issues with my task:
Timestamp session format:
It turned out that this line in our task:
TIMESTAMP_INPUT_FORMAT = 'YYYY-MM-DD HH24'
is setting the timestamp of the session into a format that is different than the format within our data.
CREATE OR REPLACE TASK DATA_LOAD_TASK
WAREHOUSE = 'COMPUTE_WH'
// TIMESTAMP_INPUT_FORMAT = 'YYYY-MM-DD HH24'
SCHEDULE = 'USING CRON 00 18 * * * Europe/Zurich'
AS
CALL proc1();
As mentioned in the Snowflake parameters documentation, this line will set the format during the whole session into the specified format in the task.
By not specifying the format of this parameter, it will set to AUTO, which will leave to Snowflake to make necessary timestamp conversions according to field types.
Ownership and usage over procedures and tasks:
To work properly with task that is calling a procedure which calling many other, we should grant usage over all used procedures to the role owning the task. Even if we are calling one procedure.
grant usage on procedure proc1(varchar, varchar, varchar, array) to role prod_taskadmin;
grant usage on procedure proc2(varchar, varchar, varchar, varchar) to role prod_taskadmin;
grant usage on procedure proc3(varchar, varchar, varchar, varchar) to role prod_taskadmin;
And the procedures should be owned by a role who have permissions over all the objects in a schema:
grant ownership on procedure proc1(string, string, string, array) to role prod_sysadmin;
grant ownership on procedure proc2(string, string, string, array) to role prod_sysadmin;
grant ownership on procedure proc3(string, string, string, array) to role prod_sysadmin;

Related

can we insert or update data in table from RLS policy in Postgres?

I've created table "clientID_dbUser_mapping" which contains client id (e.g 1,2,3) and DB User name (u1,u2,u3)
Now created another table "test_data" which contains id(PK), data(text), client_id(FK)
created RLS policy for "test_data" to access the appropriate logged in db_user data
here is policy :
CREATE POLICY client_id_testdata_policy ON test_data
FOR ALL
USING (client_id =
(SELECT client_id
FROM client_dbuser_mapping
WHERE db_user_name = current_user)
)
WITH CHECK (client_id =
(SELECT client_id
FROM client_dbuser_mapping
WHERE db_user_name = current_user)
);
This is working fine for SELECT/UPDATE/DELETE
for INSERT I want to add default data of client_id in "test_data" from "clientID_dbUser_mapping" table coz it contains client id as per db_user_name
so how can I achieve this through RLS policy (can i change something in my existing policy) or need to create new policy ?
is it possible or not?
I am not sure if I understood the requirement correctly, but if you want to force the client_id to a certain value upon INSERT, use a trigger:
CREATE FUNCTION set_client_id() RETURNS trigger
LANGUAGE plpgsql AS
$$BEGIN
SELECT cdm.client_id INTO NEW.client_id
FROM client_dbuser_mapping AS cdm
WHERE cdm.db_user_name = current_user;
RETURN NEW;
END;$$;
CREATE TRIGGER set_client_id BEFORE INSERT ON test_data
FOR EACH ROW EXECUTE PROCEDURE set_client_id();

How to log query for single table

Have simple insert with returning id
INSERT INTO "public"."mytbl" ("col1", "col2" , "col3" , "col4", "col5")
values ('test', 'test' , null , 'test', null) RETURNING id;
want log execution time for only this query. How to do it ? I dont want log all querys ,only this one
If you use the command line interface psql, you can simply enter
\timing
and you'll get the duration of each statement after it has been executed.
If you want to log the duration to the PostgreSQL log file, you can do that by setting the parameter log_min_duration_statement = 0.
The problem is that this parameter can only be set by the superuser, and you probably want to run the statement as a different user.
One way to enable a normal user to log statement duration is to use a SECURITY DEFINER function that is owned by a superuser and sets the parameter for the duration of the current transaction:
CREATE OR REPLACE FUNCTION logduration() RETURNS void AS
'SET LOCAL log_min_duration_statement=0'
LANGUAGE sql SECURITY DEFINER;
REVOKE EXECUTE ON FUNCTION logduration() FROM PUBLIC;
GRANT EXECUTE ON FUNCTION logduration() TO laurenz;
Then user laurenz can do the following:
START TRANSACTION;
SELECT logduration();
INSERT INTO "public"."mytbl" ...
COMMIT;
Your log will contain only the duration of the INSERT command (well, also the duration of the funxtion call, but that shouldn't be a problem).

Calling function which has no permission from another function

Say we have two functions. First should check permissions and if all goes right update table. Here it is:
CREATE OR REPLACE FUNCTION public.clients_test(
_clientid int
,_comments varchar
)
RETURNS void AS
$BODY$
declare _result varchar;
BEGIN
if now()::time>'17:00'::time then
select public.clients_check_17_00() into _result;
end if;
update clients set comments=_comments where clientid=_clientid;
END
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 1;
ALTER FUNCTION public.clients_test(int, varchar) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_test(int, varchar) TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_test(int, varchar) TO "RestrictedAccess";
REVOKE ALL ON FUNCTION public.clients_test(int, varchar) FROM public;
Second function doing nothing with database and exists only for security reasons. I was going to call it from the fist one. Here it is:
CREATE OR REPLACE FUNCTION public.clients_check_17_00()
RETURNS void AS
$BODY$
BEGIN
END
$BODY$
LANGUAGE plpgsql IMMUTABLE SECURITY DEFINER
COST 1;
ALTER FUNCTION public.clients_check_17_00()OWNER TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_check_17_00() TO postgres;
GRANT EXECUTE ON FUNCTION public.clients_check_17_00() TO "FullAccess";
REVOKE ALL ON FUNCTION public.clients_check_17_00() FROM public;
Some users should have to update comments only before 17:00. So they have permissions on
public.clients_test
and have no permissions on
public.clients_check_17_00
I'd like to have the error 'You do not have permissions to execute public.clients_check_17_00', but this did not work.
This is an interesting problem, but your approach is all wrong.
Rather than checking for the time inside the function, you should manipulate permissions outside of the function. In your approach, the time is checked on every call, also at 08:02, 14:23, 16:34, etc, which is very inefficient for the obvious reasons. Instead, make two simple SQL script files, one to disable execute permissions on the function and another to enable those permissions again and have a scheduled job run those scripts at 17:00 and then presumably at 08:00 or so to re-enable execute permission. It could be as simple as:
psql -h localhost -d your_db -U postgres \
-c 'REVOKE EXECUTE ON FUNCTION clients_test(int, varchar) FROM "RestrictedAccess"'
And vice-versa for enabling the permission again. But see the documentation for parameters specific to accessing your server.
How exactly that works depends on your OS; in Linux you would use a cron job, on Windows you use Task Scheduler.
Incidentally, returning a message from a function to the session is done with RAISE NOTICE.
Another important point from your code: NEVER USE THE postgres ROLE AS OWNER OF OBJECTS OR TO RUN CODE. Apologies for EMPHASIS but this point can not be stressed enough. You should instead make a non-privileged role, preferably without login privilege, as owner of all objects (tables and functions, etc) and then explicitly set privileges for specific user roles.

SQLDependency SQL Server 2008 R2

I have been trying to set up SQL Notifications on SQL Server 2008 R2 but I keep getting the 'Cannot find the specified user 'owner'' error as mentioned in the articles below.
I know several people have submitted answers to this and I've looked at all the various combinations such as
SQLDependency Caching not working
http://blogs.msdn.com/b/dataaccess/archive/2005/09/27/474447.aspx
http://keithelder.net/2009/01/20/sqldependency-and-sql-service-broker-permissions/
http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen
https://social.msdn.microsoft.com/Forums/en-US/99321f54-1fef-4860-9fe9-5966a46fe582/once-for-all-right-permissions-for-sqldependency-please?forum=sqlservicebroker
https://dba.stackexchange.com/questions/47567/permissions-using-net-sqldependency
Some of these use a user account for the permissions and others use a role.
Here's the script that I am using:
CREATE LOGIN risk_test WITH PASSWORD = 'Password1', CHECK_POLICY = OFF;
GO
CREATE USER risk_test FOR LOGIN risk_test;
GO
CREATE ROLE [sql_dependency] AUTHORIZATION [dbo];
GO
CREATE SCHEMA [sql_dependency] AUTHORIZATION [sql_dependency]
GO
EXECUTE sp_addrolemember N'sql_dependency', N'risk_test';
GO
ALTER USER [risk_test] WITH DEFAULT_SCHEMA=[sql_dependency]
GO
--Database level permissions
GRANT SELECT TO [sql_dependency];
GRANT CREATE PROCEDURE TO [sql_dependency];
GRANT CREATE QUEUE TO [sql_dependency];
GRANT CREATE SERVICE to [sql_dependency];
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO [sql_dependency];
GRANT VIEW DEFINITION TO [sql_dependency];
GRANT ALTER ON SCHEMA::sql_dependency TO [risk_test]
GO
--Service broker permissions
GRANT REFERENCES ON CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification] TO [sql_dependency];
GRANT RECEIVE ON QueryNotificationErrorsQueue TO [sql_dependency];
GO
GRANT IMPERSONATE ON USER::dbo TO [risk_test];
GO
I ran a profiler and see the following sequence:
select is_broker_enabled from sys.databases where database_id=db_id()
CREATE PROCEDURE [SqlQueryNotificationStoredProcedure-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] AS BEGIN BEGIN TRANSACTION; RECEIVE TOP(0) conversation_handle FROM [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7]; IF (SELECT COUNT(*) FROM [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] WHERE message_type_name = 'http://schemas.microsoft.com/SQL/ServiceBroker/DialogTimer') > 0 BEGIN if ((SELECT COUNT(*) FROM sys.services WHERE name = 'SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7') > 0) DROP SERVICE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7]; if (OBJECT_ID('SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7', 'SQ') IS NOT NULL) DROP QUEUE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7]; DROP PROCEDURE [SqlQueryNotificationStoredProcedure-778b1ff4-6d73-46d6-bee9-fc05272fe8d7]; END COMMIT TRANSACTION; END
declare #p3 uniqueidentifier
set #p3=NULL
exec sp_executesql N'IF OBJECT_ID(''SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7'', ''SQ'') IS NULL BEGIN CREATE QUEUE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] WITH ACTIVATION (PROCEDURE_NAME=[SqlQueryNotificationStoredProcedure-778b1ff4-6d73-46d6-bee9-fc05272fe8d7], MAX_QUEUE_READERS=1, EXECUTE AS OWNER); END; IF (SELECT COUNT(*) FROM sys.services WHERE NAME=''SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7'') = 0 BEGIN CREATE SERVICE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] ON QUEUE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] ([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]); IF (SELECT COUNT(*) FROM sys.database_principals WHERE name=''sql_dependency_subscriber'' AND type=''R'') <> 0 BEGIN GRANT SEND ON SERVICE::[SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] TO sql_dependency_subscriber; END; END; BEGIN DIALOG #dialog_handle FROM SERVICE [SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7] TO SERVICE ''SqlQueryNotificationService-778b1ff4-6d73-46d6-bee9-fc05272fe8d7''',N'#dialog_handle uniqueidentifier output',#dialog_handle=#p3 output
select #p3
We do not want to add the grant control on dbo to our user as this opens a security hole.
Does anyone know what is missing from my script to make this work?
http://www.codeproject.com/Articles/12862/Minimum-Database-Permissions-Required-for-SqlDepen
Has good advice:
"It is vital that we create a schema specifically for [risk_test] and that we make this user the owner of this schema. We also need to make sure that the default schema of this user is set to this new schema. If we wouldn't do this, then SqlDependency.Start would attempt to create some queues and stored procedures in the user's default schema which is dbo. This would fail since [risk_test] does not have sufficient rights to control the dbo-schema. Since we want to know the minimum rights [risk_test] needs to run SqlDependency.Start, we don't want to give him dbo priviliges. Creating a separate schema ensures that SqlDependency.Start can create the necessary objects inside this [risk_test] schema without compromising security."
So my advice - ensure that the connection string to the database uses the [risk_test] credentials.

Execute triggers function of another schema on the actual chema

my problem is easy to explain with an example: I have a 'common' schema (the public one?) where I store common data between a clustered application.
For every instance of my application, I have a role (used as the application user).
And i have a common role, app_users, with read-only privileges on the common schema, and every application role is a member of app_users.
Now my problem is: how can i set a trigger on the app_a scheme that execute a function (procedure) in the common scheme, but affect the (and only the) app_a tables?
I mean:
// common_scheme, dummy function to emulate the mysql on update = now()
CREATE OR REPLACEFUNCTION update_etime() RETURNS TRIGGER AS $$
BEGIN
NEW.etime = date_part('epoch'::text, now())::int;
RETURN NEW;
END;
$$ language plpgsql;
// now, in the app_foo scheme, i have the table:
CREATE TABLE foo_table (fid serial not null primary key unique, label char(25));
// and the trigger:
CREATE TRIGGER foo_table_update_etime BEFORE UPDATE ON foo_talbe FOR EACH ROW EXECUTE PROCEDURE update_etime();
// ERROR: function update_etime() does not exist
CREATE TRIGGER foo_table_update_etime BEFORE UPDATE ON foo_talbe FOR EACH ROW EXECUTE PROCEDURE common_scheme.update_etime();
// ERROR: function common_scheme.update_etime() does not exist
The user that will access app_foo has the execute privilege on update_etime() function in common_schema.
Any idea?
I've googled around but the only solution I fount to call functions from other schemas is something like execute 'select * from ' || schema_name || '.table_name'; but i dont think this will do the trick in my case, becose the function must work with the 'local' scheme.
Your second set of syntax should work... the one with "EXECUTE PROCEDURE common_scheme.update_etime();"
If it isn't finding the function, I'd guess that you either have created it in a different schema than you think it is in, or you haven't created it at all (and note, your example create syntax has a bug, no space between "replace" and "function", which would cause an error when trying to create the function. Try doing a:
\df *.update_etime
As superuser to verify the function exists and is in the location you think it is in. HTH.