:setvar ShadowSourceLogin "ft546_shadow_ro" :setvar ShadowSourceUser "ft546_shadow_ro" :setvar ShadowSourcePassword "REPLACE_AT_DEPLOY_TIME" :setvar ShadowSourceDatabase "FT546-wcs" SET NOCOUNT ON; SET XACT_ABORT ON; IF N'$(ShadowSourcePassword)' = N'REPLACE_AT_DEPLOY_TIME' THROW 51000, 'Supply ShadowSourcePassword through the approved secret channel.', 1; IF DB_ID() <> DB_ID(N'$(ShadowSourceDatabase)') THROW 51001, 'Run the shadow source-login script only in ShadowSourceDatabase.', 1; DECLARE @sourceLoginCreated bit = 0; IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'$(ShadowSourceLogin)') BEGIN DECLARE @createLogin nvarchar(max) = N'CREATE LOGIN ' + QUOTENAME(N'$(ShadowSourceLogin)') + N' WITH PASSWORD = N''' + REPLACE(N'$(ShadowSourcePassword)', N'''', N'''''') + N''', CHECK_POLICY = ON, CHECK_EXPIRATION = ON'; EXEC sys.sp_executesql @createLogin; SET @sourceLoginCreated = 1; END; BEGIN TRY IF EXISTS ( SELECT 1 FROM sys.server_role_members rm JOIN sys.server_principals role_principal ON role_principal.principal_id = rm.role_principal_id JOIN sys.server_principals login_principal ON login_principal.principal_id = rm.member_principal_id WHERE login_principal.name = N'$(ShadowSourceLogin)' ) THROW 51002, 'The shadow source login must not belong to a server role.', 1; DECLARE @serverContextSet bit = 0; BEGIN TRY EXECUTE AS LOGIN = N'$(ShadowSourceLogin)'; SET @serverContextSet = 1; IF EXISTS ( SELECT 1 FROM sys.fn_my_permissions(NULL, N'SERVER') WHERE permission_name NOT IN (N'CONNECT SQL', N'VIEW ANY DATABASE') ) THROW 51007, 'The shadow source login retains forbidden server permissions.', 1; REVERT; SET @serverContextSet = 0; END TRY BEGIN CATCH IF @serverContextSet = 1 REVERT; THROW; END CATCH; BEGIN TRANSACTION; IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'$(ShadowSourceUser)') BEGIN DECLARE @createUser nvarchar(max) = N'CREATE USER ' + QUOTENAME(N'$(ShadowSourceUser)') + N' FOR LOGIN ' + QUOTENAME(N'$(ShadowSourceLogin)'); EXEC sys.sp_executesql @createUser; END ELSE IF SUSER_SID(N'$(ShadowSourceLogin)') <> USER_SID(N'$(ShadowSourceUser)') THROW 51003, 'The existing shadow source user maps to a different login.', 1; DECLARE @principal nvarchar(258) = QUOTENAME(N'$(ShadowSourceUser)'); DECLARE @permissions nvarchar(max) = N'GRANT CONNECT TO ' + @principal + N';' + N'REVOKE CONTROL FROM ' + @principal + N';' + N'REVOKE SELECT FROM ' + @principal + N';' + N'GRANT SELECT ON SCHEMA::shadow_ro TO ' + @principal + N';' + N'DENY INSERT, UPDATE, DELETE, EXECUTE, ALTER, TAKE OWNERSHIP ' + N'TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.wcs_srgv_queue TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.wcs_srgv_queue_finish TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.device_srgv TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.wcs_srgv_map TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.bas_cargo_space TO ' + @principal + N';' + N'DENY CONTROL ON OBJECT::dbo.tsj_device_execution_queue TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.wcs_srgv_queue TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.wcs_srgv_queue_finish TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.device_srgv TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.wcs_srgv_map TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.bas_cargo_space TO ' + @principal + N';' + N'DENY SELECT ON OBJECT::dbo.tsj_device_execution_queue TO ' + @principal + N';'; EXEC sys.sp_executesql @permissions; DECLARE @role sysname; DECLARE forbidden_roles CURSOR LOCAL FAST_FORWARD FOR SELECT role_principal.name FROM sys.database_principals role_principal JOIN sys.database_role_members rm ON rm.role_principal_id = role_principal.principal_id JOIN sys.database_principals member_principal ON member_principal.principal_id = rm.member_principal_id WHERE role_principal.type = 'R' AND member_principal.name = N'$(ShadowSourceUser)'; OPEN forbidden_roles; FETCH NEXT FROM forbidden_roles INTO @role; WHILE @@FETCH_STATUS = 0 BEGIN IF IS_ROLEMEMBER(@role, N'$(ShadowSourceUser)') = 1 BEGIN DECLARE @dropRole nvarchar(max) = N'ALTER ROLE ' + QUOTENAME(@role) + N' DROP MEMBER ' + @principal; EXEC sys.sp_executesql @dropRole; END; FETCH NEXT FROM forbidden_roles INTO @role; END; CLOSE forbidden_roles; DEALLOCATE forbidden_roles; DECLARE @databaseContextSet bit = 0; BEGIN TRY EXECUTE AS USER = N'$(ShadowSourceUser)'; SET @databaseContextSet = 1; IF HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'SELECT') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'INSERT') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'UPDATE') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'DELETE') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'EXECUTE') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'ALTER') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'CONTROL') <> 0 OR HAS_PERMS_BY_NAME(DB_NAME(), N'DATABASE', N'TAKE OWNERSHIP') <> 0 THROW 51004, 'The shadow source login retains forbidden database permissions.', 1; IF EXISTS ( SELECT 1 FROM sys.schemas s CROSS APPLY sys.fn_my_permissions(QUOTENAME(s.name), N'SCHEMA') p WHERE s.name NOT IN (N'sys', N'INFORMATION_SCHEMA') AND NOT (s.name = N'shadow_ro' AND p.permission_name = N'SELECT') ) THROW 51005, 'The shadow source login retains forbidden schema permissions.', 1; IF EXISTS ( SELECT 1 FROM sys.objects o JOIN sys.schemas s ON s.schema_id = o.schema_id CROSS APPLY sys.fn_my_permissions( QUOTENAME(s.name) + N'.' + QUOTENAME(o.name), N'OBJECT') p WHERE o.is_ms_shipped = 0 AND NOT ( s.name = N'shadow_ro' AND o.name IN ( N'v_wcs_srgv_queue', N'v_wcs_srgv_queue_finish', N'v_device_srgv', N'v_wcs_srgv_map', N'v_bas_cargo_space', N'v_tsj_execution', N'v_current_database_clock') AND p.permission_name = N'SELECT' ) ) THROW 51006, 'The shadow source login retains forbidden object permissions.', 1; SELECT TOP (0) * FROM shadow_ro.v_wcs_srgv_queue; SELECT TOP (0) * FROM shadow_ro.v_wcs_srgv_queue_finish; SELECT TOP (0) * FROM shadow_ro.v_device_srgv; SELECT TOP (0) * FROM shadow_ro.v_wcs_srgv_map; SELECT TOP (0) * FROM shadow_ro.v_bas_cargo_space; SELECT TOP (0) * FROM shadow_ro.v_tsj_execution; SELECT TOP (0) * FROM shadow_ro.v_current_database_clock; REVERT; SET @databaseContextSet = 0; END TRY BEGIN CATCH IF @databaseContextSet = 1 REVERT; THROW; END CATCH; COMMIT TRANSACTION; END TRY BEGIN CATCH IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; IF @sourceLoginCreated = 1 BEGIN DECLARE @dropSourceLogin nvarchar(max) = N'DROP LOGIN ' + QUOTENAME(N'$(ShadowSourceLogin)'); EXEC sys.sp_executesql @dropSourceLogin; END; THROW; END CATCH;