CREATE USER in Dynamic SQL on SQL Azure - sql

I want to write a stored procedure that checks the environment a database resides in (Based on the name) and creates the appropriate user and role for our application.
This would allow us to automate setting up permissions if we move a database between environments (Currently due to the limitations of Windows Azure SQL Database we have to manually run a script which is not ideal and prone to human error).
So the syntax we are using is:
DECLARE #UserToAdd VARCHAR(50) = (
SELECT CASE
WHEN #Environment = 'Development' THEN 'DevelopmentApplicationUser'
WHEN #Environment = 'Test' THEN 'TestingApplicationUser'
ELSE ''
END
)
IF (#UserToAdd != '')
BEGIN
EXEC ('CREATE USER [' + #UserToAdd + '] FOR LOGIN [' + #UserToAdd + '];')
EXEC ('EXEC sp_addrolemember N''WebUser'', N''' + #UserToAdd + ''';')
END
This works correctly on our development server (SQL Server 2008 R2) but in Windows Azure SQL Database we get the below error:
The CREATE USER statement must be the only statement in the batch
Now the MSDN documentation does state:
If the CREATE USER statement is the only statement in a SQL batch, Windows Azure SQL Database supports the FOR | FROM LOGIN clause. If the CREATE USER statement is not the only statement in a SQL batch or is executed in dynamic SQL, the FOR | FROM LOGIN clause is not supported.
However this means that we cannot automate our permissions whatsoever.
Has anyone got around this issue and been able to produce dynamic sql that creates a user? Alternatively is there a way around this in a stored procedure?

I opened a support case with Microsoft to see if this is possible and the response was that if you want to check the existence of a login or a user and then create that login or user, you MUST use separate connections for the check and then the creation. It is NOT possible to check and create if not exists in the same transaction or batch. In Sql Azure.
Hth,
Oli

I speak under correction here.
you can create the user without FOR LOGIN
then use sp_change_users_login to map the user to a login after the fact

Related

SQL Trigger to grant 'dbowner' permissions upon new DB creation

I put together the following SQL script to create a trigger when a new DB is created, to grant db_owner permissions to a specific account:
CREATE TRIGGER ddl_trig_database
ON ALL SERVER
FOR CREATE_DATABASE
AS
DECLARE #DatabaseName NVARCHAR(128), #SQL NVARCHAR(4000)
SELECT #DatabaseName = EVENTDATA().value('(/EVENT_INSTANCE/DatabaseName)[1]','NVARCHAR(128)');
SET #SQL = '
USE ' + #DatabaseName + ';
EXEC sp_addrolemember N''db_owner'', N''[accountname]'';'
EXEC(#SQL)
However, I get the following error when I try to create a new DB to test this Trigger:
Message: User or role '[accountname]' does not exist in this database.
Could not find database ID 45, name '45'. The database may be offline.
Wait a few minutes and try again.
I put this together using some examples found on the web. It appears that the Trigger is occurring right when DB is being created, instead of running after DB has been created. Is there a way to delay it?
Few things to be mentioned here:
The trigger is executed AFTER the database is created as this is the default behaviour
Not being able to find the database may point to lack of required permissions. Make sure you have enough permission by impersonating as a user that has been granted enough permissions i.e. some database owner. Use the WITH EXECUSE AS clause.
Make sure you have the "[accountname]" existing at the new DB or at the server level (depends what kind of account you are trying to add). You can add a database user, database role, Windows login, or Windows group.
References:
https://msdn.microsoft.com/en-us/library/ms189799.aspx
https://msdn.microsoft.com/en-us/library/ms187750.aspx
SQL Server 2008 - Does a trigger run with the same permissions as the login/user?

sp_setapprole and creating users / logins

We have set up SQL Server and our Application in such a way that an SQL Server login has … well no permissions when accessing the database using any tool, except our application. This is needed because some people might fire up access (or SQL Server Management Studio) and would be able to manipulate the data in that way.
So we set up an Application Role and gave that Application Role the necessary permissions. Everything works nicely, users can't access our tables unless they use our Application.
Now comes the problem. In our application it should be possible to Create additional logins / users, or set some specific users as being Administrators (only admins can do user related stuff). I'm not 100% certain but in that case I think the user needs SecurityAdmin role. So we have some code in our application which looks like this :
IF NOT EXISTS ( SELECT name
FROM sys.server_principals
WHERE name = #LoginName )
BEGIN
SET #SQL = 'CREATE LOGIN [' + #LoginName + '] WITH PASSWORD = '''
+ #Password + ''', DEFAULT_DATABASE=[' + #DBNAME
+ '], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF';
EXECUTE(#SQL);
END
IF NOT EXISTS ( SELECT name
FROM sys.database_principals
WHERE name = #LoginName )
BEGIN
SET #SQL = 'CREATE USER [' + #LoginName + '] FOR LOGIN ['
+ #LoginName + ']';
EXECUTE(#SQL);
END
IF EXISTS ( SELECT name
FROM sys.server_principals
WHERE name = #LoginName )
BEGIN
EXEC sp_addsrvrolemember #LoginName, 'securityadmin'
END
The problem is of course that the server_principals is in the Master Database, and there is no way I can give my Application Role those permissions. Is there any way to work-around this problem or an easy way to solve this ?
In SQL Server, when you activate an Application Role (AppRole) , its security context completely supplants the security context of the User. Although this can be reverted in the session(connection), the two security contexts (AppRole and User) cannot both be active at the same time.
Therefore, the usual way to use these two together is exactly how you have done it so far(*): that is, use the User/Login only to enable the right to connect to the database initially, then switch to the AppRole, to gain access to the databases contents.
However, if you want to enable different levels of permissions/rights within the database, you cannot do it through the Users/Logins (not solely anyway). There are several different ways to approach this, but the simplest way to accomplish what you want is probably to have different AppRoles for different classes of application users. So you could have an ApplUser role for most users, and an ApplAdmin role for users who are administrators, etc. You would then grant these additional AppRoles, enhanced permissions and access within the database, as needed.
(* And big kudos for that. This is a very good security scheme for SQL Server, but few application developers go through the effort to implement it.)
You should have a look at the EXECUTE AS clause in SQL Server:
EXECUTE AS Clause
You can then put the logic into a stored procedure in the database, set this to execute as a different user (or owner if the owner of the database has the permissions to perform the operation). All you need to do then is give the Application Role the permissions to execute the procedure, nothing more and the code itself will be executed as the other user.

SQL - Impersonate SYSTEM_USER

Is there a way to impersonate or change the SYSTEM_USER on MS SQL 2005?
I have many views (written by a third party) which I can not change which references an SYSTEM_USER to "ID Table".
... AND idCode = SUBSTRING(SYSTEM_USER, CHARINDEX('\', SYSTEM_USER) + 1, LEN(SYSTEM_USER))
*I do have rights to the tables that the views pull from, BUT these views have the added SYSTEM_USER.*
Thanks.
Check out the Execute As Transact SQL
Providing you have the correct permissions you can execute any T-SQL as another user and then revert back to the original connection credentials.
Select System_User
Go
Execute As Login = 'SomeOtherLogin'
Select System_User
Revert
Go
Select System_User
Go
This will output the current connection credentials for the first and third select and output the specified credentials for the second select.

t-sql create user and grant execute on permission for stored procedures

I have a script which creates a database, stored procs, views, tables, udf. I want to include a script to create a user 'user_1' and give execute permission on the database.
I tried following to create grant exec command for all stored procs
declare #permission varchar(max)
select #permission = COALESCE(
#permission + '; ' + 'Grant Execute on ' + name + ' user_1',
'Grant Execute on ' + name + ' user_1')
from sysobjects where xtype in ('P')
exec (#permission)
But exec (#permission) does not work. It gives
incorrect syntax near ';'.
How can I solve this?
Create Login: creates the server level login. Then...
Create User: lets the Login account attach to your database. Then...
Grant Execute To: grants execute rights to ALL of the sp's and functions in your db. Use "Grant Execute ON abc TO xyz" if you only want to grant rights to specific sps.
Create login abacadaba with password='ABVDe12341234';
Create user abacadaba for login abacadaba;
Grant Execute to abacadaba;
Have you tried:
CREATE LOGIN TestUser WITH PASSWORD = 'TopSecret'
GRANT EXEC ON MyStoredProc TO TestUser
you can also "CREATE USER" if that's what you want.
Had exactly the same problem as original user, but for me it was bad characters embedded in the TSQL - I'm guessing from whatever source it was cut and pasted from.
Anyway depending on how much whitespace you have, just delete the whitespace between the words and replace with regular spaces.
Try all the other answers before this, it's fairly unlikely - I'm only adding it as it was so frustrating having 2 lines of TSQL that looked identical above/below each other, but resulted in different result messages when highlighted and run in Management Studio...
UPDATE: The bad characters were pasted from Microsoft Lync

The server principal "XYuser" is not able to access the database "Ydb" under the current security context

System Specifications
Microsoft SQL Server Management Studio 9.00.4035.00
Microsoft Analysis Services Client Tools 2005.090.4035.00
Microsoft Data Access Components (MDAC) 2000.085.1132.00
(xpsp.080413-0852)
Microsoft MSXML 2.6 3.0 4.0 5.0 6.0
Microsoft Internet Explorer 7.0.5730.13
Microsoft .NET Framework 2.0.50727.1433
Operating System 5.1.2600
On an SQL Server 2005 called BHAVMSQL02, I have two databases Mattercentre_dev and CMSNET_DEV. The Mattercentre_dev has a stored procedure that builds a list from a table in CMSNET_DEV. The stored procedure looks
like this...
USE [Mattercentre_dev]
GO
/****** Object: StoredProcedure [dbo].[UDSPRBHPRIMBUSTYPE]
Script Date:02/12/2009 10:18:10 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER OFF
GO
ALTER PROCEDURE [dbo].[UDSPRBHPRIMBUSTYPE] WITH EXECUTE AS 'Readuser' AS
DECLARE #SERVERNAME nvarchar(30)
DECLARE #DBASE nvarchar(30)
DECLARE #SQL nvarchar(2000)
SET #SERVERNAME = Convert(nvarchar,
(SELECT spData FROM dbSpecificData WHERE spLookup = 'CMSSERVER'))
SET #DBASE = Convert(nvarchar,
(SELECT spData FROM dbSpecificData WHERE spLookup = 'CMSDBNAME'))
SET #SQL =
'SELECT
null as Code
, ''(not specified)'' as Description
UNION SELECT
clnt_cat_code as Code
, clnt_cat_desc as Description
FROM '
+ #SERVERNAME + '.' + #DBASE + '.dbo.hbl_clnt_cat
WHERE
inactive = ''N''
ORDER BY Description'
PRINT #SQL
EXECUTE sp_executeSQL #SQL
#SERVERNAME == 'BHAVMSQL02'
#DBASE == 'CMSNET_DEV'
When the stored procedure was executed the following error message appeared...
The server principal "ReadUser" is not able to access the database "CMSNET_DEV" under the current security context.
After googling the error message, I carried out the following fix...
Deleted the user ReadUser from
BHAVMSQL02 -> Databases ->
Mattercentre_dev -> Security -> Users
Set Up ReadUser from BHAVMSQL02 ->
Security -> Logins with the following
settings...
General
Login Name - readUser
Password - xxxxxxxxxxxx
Confirm - xxxxxxxxxxxx
Default db - master
default lg - British English
Everything Else - Unset
Server Roles
Only Public Set
User Mappings
CMSNET_DEV - ReadUser - dbo
Database Role Membership - db_owner, public
Mattercentre_dev - ReadUser - dbo
Database Role Membership - db_owner, public
I then ran the following script...
ALTER DATABASE CMSNET_DEV SET TRUSTWORTHY ON
GO
ALTER DATABASE mattercentre_dev SET TRUSTWORTHY ON
GO
I re-ran the stored procedure and executed it again and I still have the same
error message.
I have looked this question up in Stack Overflow and the suggested solutions
are similar to my own.
You cannot use ownership chaining when your stored procedure contains dynamic SQL, i.e doing so breaks the ownership chain.
In order for this to work you will need to use a certificate to sign your stored procedures.
Below is a brilliant article that contains instructions for signing stored procedures.
http://www.sommarskog.se/grantperm.html
Looking at this in further detail, the fact that you are using the “execute as clause” should negate the fact that the ownership chain is broken as a result of incorporating dynamic SQL.
With this in mind, the likely hood is that for some reason, the login “ReadUser” does not have appropriate read access to the databases in question, this should not be the case however, given that the login is a member of the db_owner role in both databases. That said, if the database roles have been altered from their original state then this may not hold true.
To test that the issue is not isolated to the “ReadUser” login I would suggest creating a new SQL Server Login, and mapping the login to both databases (there by creating database logins of the same name) with appropriate read access. Then modify the stored procedure to execute as the new login.