SQLDependency SQL Server 2008 R2 - 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.

Related

Postgres: GRANT to user based on sub-query

This is a symptom of database and user names being different between my dev/staging/live environments, but is there a way to GRANT permissions to a user, determined by some kind of sub-query?
Something like this (not valid syntax):
GRANT UPDATE (my_column) ON my_table TO (SELECT CASE current_database()
WHEN 'account-dev' THEN 'c-app'
WHEN 'account-staging' THEN 'x-app'
WHEN 'account-live' THEN 'a-app'
END);
Use psql and its wonderful \gexec:
SELECT format(
'GRANT UPDATE (my_column) ON my_table TO %I;',
CASE current_database()
WHEN 'account-dev' THEN 'c-app'
WHEN 'account-staging' THEN 'x-app'
WHEN 'account-live' THEN 'a-app'
END
) \gexec
Alternatively, you can write a DO statement that uses EXECUTE to execute a dynamic statement constructed as above.

Azure SQL how to create database user by procedure?

I would like to register new user from client app, by calling webapi, where dapper will execute procedure to create new db user and store credentials in table (without password). After registration, I will change connection string with user credentials to log in.
Is it possible to dynamically create db users by executing stored procedure on Azure sql db?
I've made such query:
CREATE PROCEDURE [dbo].[AddDbUser]
#Login nvarchar(50),
#Password nvarchar(50),
#Mail nvarchar(70),
#Phone nvarchar(9),
#SQL nvarchar(1000)
AS
BEGIN
SET #SQL = 'USE [testdb] '
+ 'CREATE LOGIN '+quotename(#Login)+' WITH PASSWORD =
'+quotename(#Password,'''')+'; '
+ 'CREATE USER '+#Login +'; '
+ 'EXEC [sys].[sp_addrolemember] ''db_datareader'', '+#Login+'; '
+ 'EXEC [sys].[sp_addrolemember] ''db_datawriter'', '+#Login+'; '
+ 'GRANT EXECUTE ON SCHEMA :: [dbo] TO '+#Login+'; '
+ 'INSERT INTO [dbo].[Users] (Login, Mail, Phone) '
+ 'VALUES ('+quotename(#Login,'''')+', '+quotename(#Mail,'''')+',
'+quotename(#Phone,'''')+');'
EXEC (#SQL)
END
GO
exec [dbo].[AddDbUser] 'dawidtest', 'password', 'mail#mail.pl', '123123123', null results in message: "User must be in the master database.", but when I add USE [master] instead of USE [testdb] I get message: "USE statement is not supported to switch between databases. Use a new connection to connect to a different database.".
Anybody could provide me some tips how to solve my problem or maybe there is some other more elegant solution?
Thank you in advance!
According to the Microsoft you will have to do in two steps since the USE statement is not supported:
One thing to note is that SQL Azure does not allow the USE Transact-SQL statement, which means that you cannot create a single script to execute both the CREATE LOGIN and CREATE USER statements, since those statements need to be executed on different databases.
So follow the advice given in the message you are getting. Create two sql statements and execute them using different connections.
One that connects to the master db and creates the login and the other one that connects to the specific db and creates the user.

SQL Server 2012 - Permission for access to sys.dm_db_index_usage_stats in Contained Database

In a contained database, how to I grant users read access on this table?
I am trying to create a function to get the date a table was last updated, but when contained users run it, it fails with error 297 ‘The user does not have permission to perform this action.’.
CREATE FUNCTION [MGMT].[GetLastObjectUpdateTime] (#ObjectName NVARCHAR(100))
RETURNS DATETIME
WITH EXECUTE AS OWNER
AS
BEGIN
DECLARE #Result DATETIME
SELECT #result = MAX(last_user_update)
FROM sys.dm_db_index_usage_stats
WHERE OBJECT_ID = OBJECT_ID(#ObjectName)
RETURN #Result
END
(code from : http://blog.sqlauthority.com/2009/05/09/sql-server-find-last-date-time-updated-for-any-table/#comment-684267 )
I would try granting the owner the server permission VIEW SERVER STATE which is needed to use that view.
USE master;
GO
GRANT VIEW SERVER STATE TO [owner login]
GO

how to create duplicate role of a user in postgres

I need a new user but it should be granted all those privileges that the other existing user/role has.
e.g.
User A has SELECT privileges on Table1
User A has EXECUTE privileges on Table2
...
If a new User B is created, I need the same privileges as,
User B has SELECT privileges on Table1
User B has EXECUTE privileges on Table2
...
Dont ask why :/
Actually User A has custom privileges on different tables, schemas, and functions; so its very tedious and lengthy process to manually grant permissions to the new user. Any help would be good.
Try something like:
GRANT A TO B;
It will grant all right of role A to B.
For details read this chapter of the manual.
First understand that roles and users are the same thing. In fact there isn't a thing called a user really, it's just a ROLE with a LOGIN option.
Second roles can be granted to other roles.
Third priviledges on roles can be inherited.
So assuming you have created your user a like:
CREATE ROLE A LOGIN;
GRANT SELECT ON table1 TO a;
GRANT EXECUTE ON FUNCTION xxx TO a;
You should be able to create a second role that mirrors the first role like:
CREATE ROLE b LOGIN;
GRANT a TO b;
I had to write the pgpsql code to loop through the privileges of User A and grant it to User B. It was done without any problem.
create or replace function update_user_privileges() returns text as
$$
declare
info record;
str text;
begin
/*Grant privileges to user B the same as with user A for a given table schema*/
str:='';
FOR info IN
select * from information_schema.table_privileges where table_schema='public' and grantee = 'A'
LOOP
/*append the tables' name, for which we are assigning privileges from user A to B*/
str:= str ||info.table_name || ',';
/*this is the main statement to grant any privilege*/
execute 'GRANT '|| info.privilege_type ||' on table public.'|| info.table_name || ' to B';
END LOOP;
return str;
end
$$ language 'plpgsql';
Usage: Copy/paste this code to crate this function and then do
select update_user_privileges();
**You have to adapt it for your table-schema and table-names. Hope it helps anyone
Here's a quick way to create grant statements for newuser, by copying all grants on db mydb to grantee myuser.
pg_dump mydb -s | egrep '^(GRANT|REVOKE).+TO "myuser"' | sed -E "s/\"myuser\"/\"newuser\"/g"
Note: The -s flag makes pg_dump execute quickly, because it's only dumping schema info.
Example output
GRANT SELECT,INSERT,UPDATE ON TABLE tabl1e TO "newuser";
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE table2 TO "newuser";
GRANT ALL ON PROCEDURE myprocedure(ids bigint[]) TO "newuser";
Simply run the output SQL grants or pipe them to psql and you're all set.
I used following method to create a new user same as an existing user using Ubuntu.
Get a full dump of existing database.
Use the following command to extract every line with the user you want to clone.
cat /path/to/db_dump_file | grep "existing_user_name" >> /path/to/extract.sql
Open extract.sql with a text editor and replace existing username with new username.
Remove unwanted queries (if any).
Now you have new SQL queries to create the new user.
This worked for me just fine. Hope this will help someone.

How to revoke all group roles from login role

How to revoke all group roles from login role? Is there a way how to do this automatically?
Since you can GRANT / REVOKE several roles at once, a single DO command with dynamic SQL would be simpler / faster (set-based operations are regularly faster in RDBMS than looping):
DO
$do$
DECLARE
_role regrole := 'my_role'; -- provide valid role name here
_memberships text := (
SELECT string_agg(m.roleid::regrole::text, ', ')
FROM pg_auth_members m
WHERE m.member = _role
);
BEGIN
IF _memberships IS NULL THEN
RAISE NOTICE 'No group memberships found for role %.', _role;
ELSE
RAISE NOTICE '%',
-- EXECUTE
format('REVOKE %s FROM %s', _memberships, _role);
END IF;
END
$do$;
The code is in debug mode. Comment RAISE NOTICE '%', and un-comment EXECUTE to prime the bomb.
DO and string_agg() require Postgres 9.0 or later.
The object identifier type regrole was added with Postgres 9.5
Casting to regrole verifies role names on input and double-quotes where necessary when outputting text - so no SQL-injection possible.
Effectively executes a command like:
REVOKE role_a, role_b FROM my_user;
Doesn't break with maliciously formed role names:
REVOKE role_a, role_b, "FROM postgres; DELETE * FROM usr; --" FROM my_user;
Note the double quotes around the trick-name.
Raises a notice if no role memberships are found.
This revokes all memberships in other roles. It's all just roles to Postgres, some have the LOGIN privilege ("user roles"), others don't ("group roles").
Think need to query all the roles
select usename, rolname
from pg_user
join pg_auth_members on (pg_user.usesysid=pg_auth_members.member)
join pg_roles on (pg_roles.oid=pg_auth_members.roleid)
and LOOP through the result to REVOKE rolname FROM usename;