Creating SQL users and restricting their permissions - sql

Greetings,
My program creates a SQL database using ADO.NET APIs to connect to a SQL server and manipulate SQL connections/transactions. When the database is created, I'm connecting to the server using 'sa' credentials. This will be the only time I will connect to the database using 'sa' credentials. During this same initial connection, I need to create a couple of logins and users, so that from there on credentials of these users will be used. Now, I know how I can create logins and users, but I cant seem to get restrictions of access right. Specifically, I need to create 1 db login and user that will have access to all stored procedured created for the database - sort of like a db admin, but very restricted (to only stored procs). And the second db login/user will be given access only to a couple of specific stored procs. Any pointers? Thanks!

I would create a database role and then add the users to it. For the second user, I've included in the role and then denied permission to 2 sprocs. If you are denying permission to many sprocs and only granting permissions to a few sprocs, it may be better/easier to not include the second user in the role and individually grant permissions, for each object that you'd like to grant on. That's up to you and which way is most manageable for your situation.
--Replace db with your database name
USE db
--Create a database role
CREATE ROLE db_execonly
--Grant EXEC permissions to the role
GRANT EXECUTE TO db_execonly
--Add users to the new role
EXEC sp_addrolemember 'db_execonly', 'user1'
EXEC sp_addrolemember 'db_execonly', 'user2'
--Deny permissions to specific objects for user2
DENY EXEC ON OBJECT::dbo.usp_sproc1 TO user2
DENY EXEC ON OBJECT::dbo.usp_sproc2 TO user2

Here you can find a stored procedure that grants access to all stored procedures to a user :
MS SQL Tips
All you have to do is to call it with the user that needs to have rights to exec all stored procedures as parameter.
From this script you can see the GRANT used to allow access to a stored procedure is :
GRANT EXEC ON ' + '[' + #OwnerName + ']' + '.' + '[' + #ObjectName + ']' + ' TO ' + #user
Edit : paste the stored procedure from the link
CREATE PROCEDURE spGrantExectoAllStoredProcs #user sysname
AS
/*----------------------------------------------------------------------------
-- Object Name: spGrantExectoAllStoredProcs
-- Author: Edgewood Solutions
-- Development Date: 03.19.2007
-- Called By: TBD
-- Description: Issue GRANT EXEC statement for all stored procedures
-- based on the user name that is passed in to this stored procedure
-- Project: SQL Server Security
-- Database: User defined databases
-- Business Process: SQL Server Security
--
----------------------------------------------------------------------------
-- Num | CRF ID | Date Modified | Developer | Description
----------------------------------------------------------------------------
-- 001 | N\A | 03.15.2007 | Edgewood | Original code for the GRANT
-- EXEC process
--
--
*/
SET NOCOUNT ON
-- 1 - Variable declarations
DECLARE #CMD1 varchar(8000)
DECLARE #MAXOID int
DECLARE #OwnerName varchar(128)
DECLARE #ObjectName varchar(128)
-- 2 - Create temporary table
CREATE TABLE #StoredProcedures
(OID int IDENTITY (1,1),
StoredProcOwner varchar(128) NOT NULL,
StoredProcName varchar(128) NOT NULL)
-- 3 - Populate temporary table
INSERT INTO #StoredProcedures (StoredProcOwner, StoredProcName)
SELECT ROUTINE_SCHEMA, ROUTINE_NAME
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME NOT LIKE 'dt_%'
AND ROUTINE_TYPE = 'PROCEDURE'
-- 4 - Capture the #MAXOID value
SELECT #MAXOID = MAX(OID) FROM #StoredProcedures
-- 5 - WHILE loop
WHILE #MAXOID > 0
BEGIN
-- 6 - Initialize the variables
SELECT #OwnerName = StoredProcOwner,
#ObjectName = StoredProcName
FROM #StoredProcedures
WHERE OID = #MAXOID
-- 7 - Build the string
SELECT #CMD1 = 'GRANT EXEC ON ' + '[' + #OwnerName + ']' + '.' + '[' + #ObjectName + ']' + ' TO ' + #user
-- 8 - Execute the string
-- SELECT #CMD1
EXEC(#CMD1)
-- 9 - Decrement #MAXOID
SET #MAXOID = #MAXOID - 1
END
-- 10 - Drop the temporary table
DROP TABLE #StoredProcedures
SET NOCOUNT OFF
GO
Here it is.
All you need is to call this procedure with your sa user ;)

Related

Create users with SSMS in Azure SQL PaaS

We're new to using SQL as a service. Since there's no gui for user management, we're having a hard time figuring out how to create users who are actually able to log in with SSMS. I can create users with access to the proper DBs, and use those users to access the DBs programatically, but not through SSMS. How do I create multiple users who can login and administrate SQL as a service databases?
Connect to Azure SQL Database using the Admin user. You can automate creating many users using a script like below:
declare #Name varchar(100),#sql varchar(400),#sql2 varchar(400),#sql3 varchar(400) ,#sql4 varchar(400)
declare #psw varchar(10) = 'Traf#12345';
declare #T table (Name varchar(100)) ;
insert into #T values
('SampleUser1'),
('SampleUser2')
While exists(select * from #T)
begin
select top 1 #Name = name from #T
IF NOT EXISTS (SELECT [name] FROM [sys].[sql_logins] WHERE [name] = #Name)
BEGIN
SET #sql = 'CREATE LOGIN '+'['+#name+']'+' WITH PASSWORD ='''+ #psw + '''; '
EXEC (#sql)
END
DELETE FROM #T WHERE Name = #Name
END
Below is the sequence that you need to create users:
---- ON SERVER LEVEL
CREATE LOGIN TestUser
WITH PASSWORD = 'ThisIsAStrongPassword!'
GO
---- create user on Master database Level
CREATE USER TestUser
FOR LOGIN TestUser
WITH DEFAULT_SCHEMA = dbo
GO
---- create user on database level
CREATE USER TestUser
FOR LOGIN TestUser
WITH DEFAULT_SCHEMA = dbo
GO
-- if you want the user to be able to create databases and logins
exec sp_addRoleMember 'dbmanager', 'TestUser';
exec sp_addRoleMember 'loginmanager', 'TestUser'
-- in each individual database, to grant dbo
exec sp_addRoleMember 'db_owner', 'TestUser';
This document will be helpful for your requirement ,
https://azure.microsoft.com/en-in/blog/adding-users-to-your-sql-azure-database/

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;

SQL Grant other user db_owner when creating database

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).

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.

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)