SQL Grant other user db_owner when creating database - sql

I would like to set up a MS SQL trigger (or some other mechanism) that would automatically grant some group db_owner permission.
I don't want our users to have db_admin on the SQL Servers, instead I would like to grant them only db_creator but whenever someone creates a database, other users should have access to this database as well.
I have tried creating a SQL trigger:
USE master
GO
CREATE TRIGGER trg_DDL_CreateDatabase
ON ALL SERVER
FOR CREATE_DATABASE
AS
DECLARE #databaseName varchar(max)
SET #databaseName = (SELECT EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'VARCHAR(255)'))
EXEC ('USE [' + #databaseName + ']' )
IF IS_MEMBER ('DOMAIN\GROUP') = 1
EXEC sp_addrolemember N'db_owner', N'DOMAIN\SOMEGROUP'
ELSE
EXEC sp_addrolemember N'db_owner', N'DOMAIN\SOMEOTHERGROUP'
GO
Since I can not use USE statement in the trigger I've tried using the EXEC ('USE [' + #databaseName + ']' ) approach - however it doesn't work either.
Any suggestions please?
edit: It would be awesome if I can grant different permissions based on the group membership. For example user is member of group A then A is being added to the db_owner, if B then B,C and E is being added etc.

Every database gets created as a copy of [model]. Any users, groups and grants you apply in [model] will be automatically copied into any newly created database. Note that this does not address databases that are attached.
If you want to do it via DDL triggers you need to use dynamic sql for the entire batch:
CREATE TRIGGER trg_DDL_CreateDatabase
ON ALL SERVER
FOR CREATE_DATABASE
AS
DECLARE #databaseName sysname;
SET #databaseName = (SELECT EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]', 'sysname'));
declare #sql nvarchar(max);
set #sql = N'USE ' + quotename(#databaseName) + N';
IF IS_MEMBER (''DOMAIN\GROUP'') = 1
EXEC sp_addrolemember N''db_owner'', N''DOMAIN\SOMEGROUP''
ELSE
EXEC sp_addrolemember N''db_owner'', N''DOMAIN\SOMEOTHERGROUP'';';
exec sp_executesql #sql;
As good practices use QUOTENAME() to properly escape names. Use sysname for database name type, both in extraction (xml method) and in storage (variable type). As it were your code could had been easily exploited by a db_creator to elevate himself to sysadmin via SQL injection (create an appropriate db name and pwn you).

Related

DENY deletion for schema except one table [duplicate]

We have some 50 tables, and we need to deny write permissions to every table except for one table for particular user.
How can we do this?
Here is a way to do it with dynamic SQL. The Print may not show you the whole command because of output limitations in Management Studio. You'll need to update the username and exception.
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + '
DENY UPDATE, DELETE, INSERT ON ' +
QUOTENAME(SCHEMA_NAME([schema_id])) + '.'
+ QUOTENAME(name) + ' TO [username];' -- fix this username
FROM sys.tables
WHERE name <> 'exception'; -- fix this to be the one you want to allow
PRINT #sql;
-- EXEC sp_executesql #sql;
In SQL Server Management Studio:
Go to the properties page for the user, then the User Mapping tab.
Tick public and db_datareader (do not tick db_denydatawriter) for the appropriate database.
This will only grant them read access.
Then you can grant insert and update for the user to the table using a query similar to this:
GRANT INSERT ON OBJECT::dbo.MyTable TO Fred
GRANT UPDATE ON OBJECT::dbo.MyTable TO Fred

TSQL Stored Procedure - Create User in database from another or master database

I need to create a stored procedure (1 or more) that I can call from the master db (or other db) in a scheduled job to restore permissions (user and privs) to a database that has been restored from a source db. I do not want to create the users at the source db level because I want to restrict access to that server, so the sproc will need to be executed once the restore is completed to grant access. I currently have:
use (database);
if ( select objectproperty(object_id('dbo.spMaint_RestorePermissions_database'), 'IsProcedure') ) is null
exec ('create procedure dbo.spMaint_RestorePermissions_database as select 1');
go
alter procedure dbo.spMaint_RestorePermissions_database
as
set nocount on;
---- grant user permission to access database
create user [domain\userid] for login [domain\userid]
----- grant user access rights to database
exec sp_addrolemember 'db_datareader', 'domain\userid'
go
One of the restrictions of stored procs is that they cannot contain use database; statements. To accomplish what you're looking for, you could run sp_executesql from master and pass in a tsql string that executes sp_executesql in the context of a specific database. Something like this:
--This is the tsql statement that gets executed on a specific user db.
--You would use this to build your "create proc", "alter proc", "create user", etc statements.
DECLARE #InnerSql NVARCHAR(MAX) = 'SELECT DB_NAME()'
DECLARE #DB SYSNAME = 'DatabaseName'
DECLARE #Tsql NVARCHAR(MAX) = '[' + #DB + ']..sp_executesql N''' + #InnerSql + ''''
EXEC sp_executesql #Tsql;

Granting access to one db to users/roles of another

Short version: Can I grant access to external databases to a role?
Long version:
I am working on reports using Crystal which is retrieving data from an applications SQL Server Instance (database1).
The application is running the reports and overwriting the connection in the report, I have no access to the applications code.
I have added a new DB to the server (database2) which is collecting information from a telephone exchange and I want to join some of this information to the applications data (database1).
I can join the data and the reports work when run within the designer (logged in as SA) but when the reports are run externally through the application they fail with a fairly generic error (Failed to retrieve data).
I am assuming the error is being caused by the new DB permissions as if I log into the application as SA the error goes away.
The Application has a special DB Role for users that run reports, when adding a table/view/sp to the application db (database1) I can simply grant select/execute to this role to allow the reports to access the object.
Now I have object in a different db however the role isn't easily accessible.
Is there any way I can reference the second db (database2) through the existing role?
eg:
USE [database1]
GRANT EXECUTE ON [database2].[dbo].[CUSTOM_PROCEDURE] TO [applicationrole1]
OR
USE [database2]
GRANT EXECUTE ON [dbo].[CUSTOM_PROCEDURE] TO [database1].[dbo].[applicationrole1]
Ideally I want to be able to link to the Role somehow rather than re-creating a new role as the role is updated by the application regularly when new users are added/configured.
(Not tagged with Crystal-Reports as this isn't related to the problem)
Edit:
Is there any way to do something like:
INSERT INTO Database2.sys.database_principals
SELECT * FROM Database1.sys.database_principals
WHERE [type] = 'S'
To copy over the Users (not logins) and then add the role members?
Presumably, you'd be using a login that has access to both databases (such as the case with SA). You'd create the appropriate role and grant rights to each database, then create the user (linked to the login you're using) in both, adding each to the role you created.
The T-SQL will look something like this:
use master
go
create login testuser with password = 'mypassword123'
go
use test
go
create role reporting
grant select on something to reporting -- grant your permissions here
create user testuser for login testuser
exec sp_addrolemember 'reporting', 'testuser'
go
use test2
go
create role reporting
grant select on something2 to reporting -- grant your permissions here
create user testuser for login testuser
exec sp_addrolemember 'reporting', 'testuser'
go
Now I can connect to test and execute
select * from something
select * from test2.dbo.something2
Of course, you'd change your grants to EXECUTE on the desired stored procedures, but it looks like you've already got that covered.
After that, it's just about executing a simple script to create logins, users, and add them to the role.
declare #sql nvarchar(max), #username nvarchar(50), #password nvarchar(50)
-- ########## SET PARAMETERS HERE
SET #username = N'testguy'
SET #password = N'test123'
-- ########## END SET PARAMETERS
set #sql = N'USE master; CREATE LOGIN [' + #username + N'] WITH PASSWORD = N''' + #password + N'''; USE database1; CREATE USER [' + #username + N'] FOR LOGIN [' + #username + N']; EXEC sp_addrolemember ''reporting'', ''' + #username + N'''; USE database2; CREATE USER [' + #username + N'] FOR LOGIN [' + #username + N']; EXEC sp_addrolemember ''reporting'', ''' + #username + N''';'
exec sp_executesql #sql
Syncing logins, users, and roles automatically
This script will find all SQL logins (you can change this to whatever makes sense to you; windows AND SQL accounts, accounts that contain a certain string, whatever), ensure the user has been created in database1 and database2, and ensures they are both added to the reporting role. You will need to ensure the reporting role is created on both databases, but you only need to do this once.
After that, you can run this script periodically, either manually, or using a SQL Agent job. All you need to do is create the login for the server; when the script runs it will do the rest.
declare #login nvarchar(50), #user1 nvarchar(50), #user2 nvarchar(50), #sql nvarchar(max), #rolename nvarchar(50)
SET #rolename = 'reporting'
declare c cursor for
select sp.name as login, dp1.name as user1, dp2.name as user2 from sys.server_principals as sp
left outer join database1.sys.database_principals as dp1 on sp.sid = dp1.sid
left outer join database2.sys.database_principals as dp2 on sp.sid = dp2.sid
where sp.type = 'S'
and sp.is_disabled = 0
open c
fetch next from c into #login, #user1, #user2
while ##FETCH_STATUS = 0 begin
-- create user in db1
if (#user1 is null) begin
SET #sql = N'USE database1; CREATE USER [' + #login + N'] FOR LOGIN [' + #login + N'];'
EXEC sp_executesql #sql
end
-- ensure user is member of role in db1
SET #sql = N'USE database1; EXEC sp_addrolemember '''+ #rolename + ''', ''' + #login + N''';'
EXEC sp_executesql #sql
-- create user in db2
if (#user2 is null) begin
SET #sql = N'USE database2; CREATE USER [' + #login + N'] FOR LOGIN [' + #login + N'];'
EXEC sp_executesql #sql
end
-- ensure user is member of role in db2
SET #sql = N'USE database2; EXEC sp_addrolemember '''+ #rolename + ''', ''' + #login + N''';'
EXEC sp_executesql #sql
fetch next from c into #login, #user1, #user2
end
close c
deallocate c
You will want to add a transaction and error handling to roll off incomplete changes, but I'll leave that up to you.
Set the stored procedure to execute as owner.
http://msdn.microsoft.com/en-us/library/ms188354.aspx
Set trustworthy on the database where the stored procedure is located.
http://technet.microsoft.com/en-us/library/ms187861.aspx
Ensure that you have the same owner on both databases.
http://msdn.microsoft.com/en-us/library/ms188676(v=sql.105).aspx
Grant execute permissions on the stored procedure to the appropriate user or role on the database with the procedure.

How to rename users, add a role etc, using script in t-sql

I have to
Rename users (from yyyyyy\xxxx to xxxx)
Add a role to the users
See the priviliges of stored procedures granted to a specified role (I found a table with the information regarding tables, but not stored procedure)
All in t-sql. I know how to do it mannualy, but with 400+ users, I hope to script me out of the problems.
Can anyone help?
What you need to do is loop over the users to modify and execute the commands to make the changes you need. You can do this by querying the syslogins table and creating a cursor with the results.
I have added the statement to rename the user, but adding the role is as simple as adding in a second statement and exec with sp_addrolemember
DECLARE #Login as varchar(50);
DECLARE LoginsCrsr CURSOR FOR
SELECT name
FROM syslogins
WHERE name like '%John%'; --Whatever critera you need
OPEN LoginsCrsr;
FETCH NEXT FROM LoginsCrsr
INTO #Login;
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #TSQL as varchar(255)
DECLARE #NewLogin as varchar(50)
SELECT #NewLogin = #Login -- Do your own thing here
SELECT #TSQL = 'ALTER LOGIN [' + #Login + '] WITH NAME=[' + #NewLogin + ']'
PRINT #TSQL
EXEC (#TSQL)
--Whatever else you need to do
FETCH NEXT FROM LoginsCrsr
INTO #Login
END
GO
CLOSE LoginsCrsr;
DEALLOCATE LoginsCrsr;
GO

T-SQL statement won't accept a variable as role name in a grant statement

I want to create a stored proc in SQL Server 2008 to create database roles and grant permissions to them, that takes one parameter, an nvarchar that represents a role name that is to be created, but im not sure how to write this.
I am able to create the role with this
EXEC sp_addrole #RoleName
but when I try to grant permissions to the role with this
Grant select on dbo.someTable to #RoleName
it won't accept the #RoleName, how do I go about doing this?
thanks
Use dynamic SQL to generate the sql statement as text, which can then be run using EXEC
declare #sql nvarchar(max)
set #sql = 'Grant select on dbo.someTable to ' + #RoleName -- protect if required
EXEC (sql)