Is there a way to add a WHERE clause within the sp_addrolemember script so that I don't have to create the stored procedure in every single database?
For example in my stored procedure:
BEGIN
DECLARE #SQL NVARCHAR(MAX)
DECLARE #GrantSql INT
EXEC #GrantSql = sp_addrolemember 'db_owner', #LoginName WHERE DatabaseName = 'DBName'
IF #GrantSQL = 0
BEGIN
INSERT INTO TableName....
END
END
In documentation:
Adds a database user, database role, Windows login, or Windows group
to a database role in the CURRENT database
But you can try:
EXEC #GrantSql = DBName..sp_addrolemember 'db_owner', #LoginName
If DBName is a parameter, you should use parametrized dynamic sql
DECLARE #Sql NVARCHAR(MAX) =
N'EXEC #GrantSql = ' + QUOTENAME(#DBName) + '..sp_addrolemember #RoleName, #LoginName'
EXEC sp_executesql #Sql, N'#RoleName NVARCHAR(MAX), #LoginName NVARCHAR(255)', #RoleName, #LoginName
P.S.
Here is a good article about dynamic sql:
http://www.sommarskog.se/dynamic_sql.html
I think everyone who are about to write dynamic sql MUST read it
Related
Scenario: Instructor of a database course wants to do the following for each of 100 students every semester: (a) create login from windows, (b) create database for each student where they are db_owner, (c) each student has read access to a database.
Here is the script I have so far:
DECLARE #DBname varchar(10) = 'aa1833'
DECLARE #Lname varchar(20) = 'STUDENTS'+'\'+#DBname
DECLARE #CreateDB varchar(max) = 'CREATE DATABASE '+#DBname
EXEC(#CreateDB)
DECLARE #CreateLogin varchar(max) = 'CREATE LOGIN ['+#Lname+ '] FROM WINDOWS'
EXEC(#CreateLogin)
USE sample_database
EXEC sp_addrolemember 'db_datareader', #Lname
EXEC sp_droprolemember 'db_owner', #Lname
DECLARE #dbRights varchar(max) = 'USE '+#DBname
EXEC(#dbRights)
EXEC sp_addrolemember 'db_owner', #Lname
I would appreciate help with following issues:
The above script is able to create logins and user's database. However, access rights (to individual database and sample database) are incorrect.
To manage server space, I would like to delete student accounts and their databases at the end of each semester. Can we have database names such as students\f21\001\aa1833?
Any other comments to improve this set up (students cannot access others' work and edit contents) would be appreciated.
SQL Server 15
The database context reverts back to the outer context (sample_database) after the dynamic SQL USE statement runs so the subsequent sp_addrolemember runs in sample_database instead of aa1833 as intended.
Execute USE and sp_addrolemember in the same dynamic SQL batch to avoid the issue:
DECLARE #dbRights varchar(max) = 'USE ' + QUOTENAME(#DBname) + N';EXEC sp_addrolemember ''db_owner'', #Lname';
sp_executesql #dbRights, N'#Lname sysname', #Lname = #Lname;
We are moving to RDS and one of our apps needs access to tempdb and I am trying to figure out the best way to create a startup job that works with RDS. Currently we are able to create a stored proc that sets up the necessary permissions in the master database and use the EXEC sp_procoption 'AddPermissionsToTempDb', 'startup', 'true' command to set it to start at boot.
In RDS however we are not able to create stored procs in the master database. I tried creating the stored proc in a user-owned db but when I then try to create the startup job with EXEC sp_procoption 'mydb.dbo.AddPermissionsToTempDb', 'startup', 'true' it says it can't find the stored procedure or I do not have permission... Is there another way to accomplish this on RDS?
Was able to find a solution based on Jeroen Mostert's comment so credit goes to them. Here is the full query I used to create the startup job to grant permissions to a list of users to create, control and execute stored procedures on tempdb on an AWS RDS SQL Server instance:
USE msdb
go
declare #job_name varchar(50)
set #job_name = 'AddTempDBPermissionsOnStartup'
exec dbo.sp_delete_job #job_name = #job_name
declare #sql varchar(max)
select #sql = '
Declare #Users Table (username varchar(100) )
insert #Users(username) values (''[user1]''),
(''[user2]''),
(''[user3]'')
use tempdb
CREATE ROLE sp_executor GRANT EXECUTE TO sp_executor
CREATE ROLE sp_manipulator
GRANT CREATE PROCEDURE TO sp_manipulator
GRANT CONTROL TO sp_manipulator
DECLARE #username as NVARCHAR(100);
DECLARE User_Cursor CURSOR FOR
SELECT * from #Users
OPEN User_Cursor;
FETCH NEXT FROM User_Cursor INTO #username;
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #username
IF EXISTS(SELECT * FROM [tempdb].sys.database_principals WHERE type_desc = ''SQL_USER'' AND name = #username)
PRINT '' - user already exists''
ELSE
BEGIN
PRINT '' - creating user''
DECLARE #Sql VARCHAR(MAX)
SET #Sql =
''USE Tempdb'' + char(13) +
''CREATE USER '' + #username + '' FOR LOGIN '' + #username + char(13) +
''EXEC sp_addrolemember sp_executor, '' + #username + char(13) +
''EXEC sp_addrolemember sp_manipulator, '' + #username
EXEC (#Sql)
END
FETCH NEXT FROM User_Cursor INTO #username;
END;
CLOSE User_Cursor;
DEALLOCATE User_Cursor;
GO
'
--Add a job
EXEC dbo.sp_add_job
#job_name = #job_name ;
--Add a job step to run the command
EXEC sp_add_jobstep
#job_name = #job_name,
#step_name = N'job step',
#subsystem = N'TSQL',
#command = #sql
--Schedule the job to run at startup
exec sp_add_jobschedule #job_name = #job_name,
#name = 'RunAtStartSchedule',
#freq_type=64
--Add the job to the SQL Server Server
EXEC dbo.sp_add_jobserver
#job_name = #job_name
I have an ASP.Net MVC application which use SQL 2012 as the database server.
I have used Views,Stored Procedures (With/Without dynamic sql queries). I 've heard that dynamic sql can be a victim of sql injection.
Here is one of my sample dynamic query..
DECLARE #Username AS Varchar(100);
DECLARE #Password AS Varchar(100);
SET #Username = 'user1';
SET #Password = '123';
DECLARE #Query AS VARCHAR(MAX);
SET #Query = 'SELECT * FROM USERS WHERE Username ='+ #Username+ ' AND Password = '+#Password+';
EXEC(#Query)
How can I write this query preventing sql injection?
The premise is essentially the same in SQL as it is in application code... Never directly concatenate input as code but instead treat it as a parameter. So if your query is something like this:
SET #Query = 'SELECT * FROM USERS WHERE Username = #Username AND Password = #Password';
Then you can execute it with parameters using sp_executesql:
exec sp_executesql #Query, N'#Username varchar(100), #Password varchar(100)', #Username, #Password
I want to create a stored procedure which I can pass a parameter to for the database name and it will create a view for me.
I am just trying to save some time by not writing the same statement over and over and over for a create vew.
Below is my syntax - how could this be modified to run in a stored procedure accepting a parameter?
Alter View dbo.ForceClose
As
SELECT DISTINCT(SessionID) As CountofSessionID
FROM Database1
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN ('JJones', 'MHill', 'RMort'))
Go
And in the stored procedure accept a parameter as the database name (I know this isn't valid just trying to show an example) -- call the stored procedure like so
exec dbo.Procedure 'DBName'
And the stored procedure would then look like
#DBName varchar(100)
Select blah blah FROM' + #DBName + '
You mean this?
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
EXEC ForceClose <DBNAME>
With the View:
CREATE PROCEDURE dbo.ForceClose
(
#DBNAME NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #SQL AS NVARCHAR(255)
#SQL='CREATE VIEW VIEW_'+DBNAME+' AS SELECT DISTINCT(SessionID) As CountofSessionID
FROM ['+#DBNAME+'].[SCHEMA].[TABLE_NAME]
WHERE forceClosed IS NOT NULL
AND stillOpen IS NULL
and (userName is not null or userName IN (''JJones'', ''MHill'', ''RMort''))'
EXEC (#SQL)
END
And you can run it by:
SELECT * FROM VIEW_<DBNAME>
Im not sure what the create view is doing; the basic premise you want is dynamic SQL (and there are lots of good questions and answers on the subject. A really simple answer would be
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
Running this returns a list of the databases on your server.
If you want to create a view; would you not need to know the table to query? The basic technique is the same
declare #myparameter nvarchar(100)= 'master'
declare #myquery nvarchar(1000)
select #myquery = 'Create View myschema.vw' + #myparameter + ' as select * from ' + #myparameter + '.dbo.sysdatabases'
select #myquery
exec sp_executeSQL #myquery
If this is in an application the user permissions to do this would be very high for a general application; you might want to wrap it with an execute as permission to stop people doing too much damage.
I need to make a stored procedure which creates a user in more than one database. Something like this:
USE [database1]
CREATE USER [userLogin] FOR LOGIN [userLogin]
USE [database2]
CREATE USER [userLogin] FOR LOGIN [userLogin]
Since the CREATE USER statement does his job in the current database I need to use the USE statement to change between databases, but it can't be used inside stored procedures.
How can I do this?
Dynamic SQL
CREATE PROCEDURE spTestProc
AS
EXEC ('USE [database1]; CREATE USER [userLogin] FOR LOGIN [userLogin]')
EXEC ('USE [database2]; CREATE USER [userLogin] FOR LOGIN [userLogin]')
GO
SQL Server gives us a system stored procedure to do this. My understanding is that the recommended method would be to use sys.sp_grantdbaccess:
CREATE PROCEDURE usp_CreateTwoUSers
AS
BEGIN
-- Create a user for a login in the current DB:
Exec sp_grantdbaccess [userLogin], [name_in_db];
-- Create a user for a login in an external DB:
Exec ExternalDatabaseName.sys.sp_grantdbaccess [userLogin], [name_in_db];
END
I did it like below:
Alter Procedure testProc
#dbName varchar(50)
As
declare #var varchar(100)
set #var = 'Exec(''create table tableName(name varchar(50))'')'
Exec('Use '+ #dbName + ';' + #var)
Exec testProc 'test_db'
CREATE PROCEDURE spTestProc
AS
BEGIN
EXECUTE sp_executesql N'USE DB1 SELECT * FROM TABLE1'
EXECUTE sp_executesql N'USE DB2 SELECT * FROM Table2'
END
exec spTestProc
now it is worked.
If you're writing dynamic SQL with EXEC sp_executesql ('query1') or EXEC ('query2') this will return correct db which you want. If you're writing static SQL or your query outside of dynamic SQL quotes or parantheses it will work on master (where you create stored procedure(default is master)).
CREATE PROCEDURE master.dbo.mysp1
AS
EXEC ('USE model; SELECT DB_NAME()') -- or sp_executesql N'USE model; SELECT DB_NAME()'
--this returns 'model'
GO
CREATE PROCEDURE master.dbo.mysp2
AS
EXEC ('USE model;') -- or sp_executesql N'USE model;'
SELECT DB_NAME()
-- this returns 'master'
GO
It should be noted that if you want to use single quotes within a EXEC command, you will need to double the amount of single quotes
e.g.
EXEC ('USE [database1]; select * from Authors where name = ''John'' ')
In this example, John has 2 single quotes before and after it.
You cannot use double quotes for this type of query.
Using sp_executesql seems to work, for more info see http://msdn.microsoft.com/en-us/library/ms175170.aspx
I tested it using this and it worked fine:
CREATE PROCEDURE spTestProc
AS
BEGIN
EXECUTE sp_executesql N'USE DB1;'
SELECT * FROM TABLE1
EXECUTE sp_executesql N'USE DB2;'
SELECT * FROM Table2
END
exec spTestProc