Bulk Insert problem - sql-server

I have a stored procedure that makes bulk insert from some file:
CREATE PROCEDURE [dbo].[SP_BulkInsert] #FileName NVARCHAR(200) AS
BEGIN
DECLARE #bulkinsert NVARCHAR(1000)
SET #bulkinsert = N'BULK INSERT TblTemp FROM ''' + #FileName +
N''' WITH (FIELDTERMINATOR = '','', ROWTERMINATOR = ''\n'')'
EXEC sp_executesql #bulkinsert
RETURN ##ROWCOUNT
END
This stored procedure runs fine when I run it from SQL Server Management Studio, but when I try to run it with ExecuteNonQuery of ADO.NET I get the following error:
"The INSERT permission was denied on the object 'TblTemp', database
'TempDB', schema 'dbo'."
Important: all other stored procedures (that make SELECT/INSERT/DELETE/UPDATE) run fine from ADO.NET.
The user under which all the things are run is a member of the bulkadmin role, and also a member of a custom db_executer role (that has just EXECUTE permission).
The code runs fine for a lot of stored procedures, it's a first time that it fails..
This is the function
public static int BulkInsert(string fileName)
{
SqlParameter paramFileName = new SqlParameter("FileName", fileName);
SqlParameter paramRetValue = new SqlParameter();
paramRetValue.Direction = ParameterDirection.ReturnValue;
SqlParameter[] #parameters = { paramFileName, paramRetValue };
SqlHelper.ExecuteNonQuery(ConnectionSettings.ConnectionString,
CommandType.StoredProcedure, "SP_BulkInsert", parameters, true);
return (int)paramRetValue.Value;
}
I logging into the SSMS with the same username/password that are in the connection string on ADO.NET side..
The bottom question is, why in Management Studio the stored procedure succeed, while via ADO.NET it fails (with the above error message).

Running SQL via the sp_executesql uses different permissions than directly in the stored procedure. I would advise checking that the user which you are running the stored procedure as has (in this instance) INSERT permissions against the table "TblTemp".
To do this in Sql Server Management Studio...
expand the list of tables
right click on the appropriate one and select properties
on the "permissions" tab, click the "Add..." button.
Either type in the user or role, or "Browse..." for it.
with the user or role selected in the top table, tick "Grant" in the appropriate permissions in the "Explicit permissions for {username/role}"
click OK

The Management Studio must be running on the sa account which has all privileges, but the user that you are connecting through ADO.NET may not have the correct privileges set. You need to give the users writing permissions through the Management studio. Look in the users list.

Related

Error running stored procedure as non-sa user: EXECUTE permission was denied on the object 'sp_start_job'

I'm trying to run a stored procedure which starts up a job. When I run it as myself (as a sysadmin), it runs without a hitch, but when I run it as a custom SQL Server user ("SQLAgentUser" -- mapped to a SQL Server login), it gives the error:
EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.
SQLAgentUser is a member of all the following roles in msdb:
SQLAgentOperatorRole
SQLAgentReaderRole
SQLAgentUserRole
In addition, SQLAgentUser has the following explicit permissions granted in MSDB:
GRANT EXECUTE on sp_start_job (and when I look at effective permissions in SSMS, it says it has Execute permissions)
GRANT EXECUTE on sp_stop_job (same as above)
In MyDb (not its actual name), SQLAgentUser has EXECUTE permissions to the sqladm schema, as well as DELETE, INSERT, SELECT, and UPDATE permissions to sqladm.AgentJobsLastRun.
According to everything I found online, this SHOULD be all that's needed, but I'm still getting the error when executing as SQLAgentUser.
Here's the erroring code:
USE [MyDb]
GO
DECLARE #RC int
EXECUTE AS USER = 'SQLAgentUser'
UPDATE [sqladm].[AgentJobsLastRun]
SET RunDate = NULL
WHERE JobName = 'MonthlyJobs'
EXECUTE #RC = [sqladm].[udp_DailyJob]
REVERT
GO
If I comment out, "EXECUTE AS USER = 'SQLAgentUser'," it runs without a hitch.
...and the code inside [sqladm].[udp_DailyJob]:
SET NOCOUNT ON;
-- Insert statements for procedure here
-- First check that the monthly job has run this month (should have run at 2 AM on the first). If not, manually run the job.
DECLARE #AgentJobNameSys nvarchar(128) = N'MonthlyJobs'
DECLARE #LastRunDate datetime = COALESCE((SELECT [RunDate] FROM [sqladm].[AgentJobsLastRun] WHERE JobName = #AgentJobNameSys),DATEADD(month,-1,getdate()))
IF #LastRunDate < DATEFROMPARTS(YEAR(getdate()),MONTH(getdate()),1)
BEGIN
EXEC msdb.dbo.sp_start_job #AgentJobNameSys;
END
Running SQL Server 2019 Developer Edition on my local computer.
Many thanks! :)

How does impersonation and security permissions in tSQLt tests work?

I have a tSQLt test which I expect to fail but it runs with success. When I run the code outside the procedure, it fails as expected, but when executing the test with Run, no error occurs.
I have read the question tSQLt Testing SQL Server security permissions but the accepted answer does not solve my problem.
My test looks like this:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
--EXEC tSQLt.ExpectException
EXECUTE AS USER = 'AFDK_Reader'
select *
from sys.user_token
SELECT * FROM fn_my_permissions('AWS', 'SCHEMA')
ORDER BY subentity_name, permission_name ;
SELECT *
FROM [AWS].[ADRESSEPUNKT_HISTORIK]
REVERT
END
The role has granted select permissions on the AFDK schema only and that is the only database role membership the SQL user has.
The AFDK_Reader has no permissions to read from the AWS schema.
Can anybody tell me how to get on with my debugging? Thanks in advance.
EXECUTE AS... REVERT commands don't behave in the way you are expecting inside a stored procedure. This is a general feature of stored procedure security in SQL Server; one common use of stored procedures is to abstract permissions. The MS docs page Customizing Permissions with Impersonation in SQL Server says:
SQL Server does not check the permissions of the caller if the stored
procedure and tables have the same owner.
and that's effectively what's happening here. Even though the EXECUTE AS changes the security context, that security context isn't checked inside the stored procedure.
The docs page also says:
However, ownership chaining doesn't work if objects have different
owners or in the case of dynamic SQL.
One way to get the behaviour you're expecting would be to run the SELECT statement from inside a dynamic SQL statement, which means that the active security context is tested against the table permissions:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
EXEC tSQLt.ExpectException
EXECUTE AS USER = 'AFDK_Reader'
EXEC ('SELECT * FROM [AWS].[ADRESSEPUNKT_HISTORIK]')
REVERT
END
A alternative (better?) solution might be to use the built-in permission functions to test permissions settings through metadata. Here's one way, still using EXECUTE AS... REVERT and sys.fn_my_permission:
CREATE PROCEDURE TestSecurity.[test AFDK_Reader cannot read from AWS schema]
AS
BEGIN
EXECUTE AS USER = 'AFDK_Reader'
DECLARE #permissionCount int = (SELECT COUNT(*) FROM sys.fn_my_permissions('[AWS].[ADRESSEPUNKT_HISTORIK]', 'OBJECT') WHERE permission_name = 'SELECT' AND subentity_name = '')
REVERT
EXEC tSQLt.AssertEquals 0, #permissionCount
END

Sql Server: How to create a db-snapshot from within stored procedure for non-privileged user?

I'm trying to create a stored procedure that creates a dB-snapshot for a non privileged user.
The idea is to provide to a normal user a way to create a dB snapshot in order to run queries against it and delete the snapshot when it is done with it.
I thought it would be possible to use the 'with execute as owner" in the procedure declaration. However, I always get the following error:
CREATE DATABASE permission denied in database 'master'.
Here is my code:
-- The user that create the sp has sysadmin right
CREATE OR ALTER PROCEDURE [dbo].[makeSnapshot] WITH EXECUTE AS OWNER
AS
-- just an extract of the code (should test if exist...)
DECLARE #exec NVARCHAR(2000)
set #exec = 'CREATE DATABASE test_dbss1900 ON ( NAME = test, FILENAME =
''C:\\Program Files\\Microsoft SQL Server\\MSSQL14.SQLSERVER2017\\MSSQL\\Data\\test_1900.ss'' ) AS SNAPSHOT OF test';
EXEC (#exec)
GO
-- try to execute it (with any user)
EXEC dbo.[makeSnapshot]
Has anyone an idea how I can come up with a stored proc that will allow a normal user to create a db snapshot?
Thank for any help!
José
I actually found a solution by looking at - http://www.sommarskog.se/grantperm.html#serverlevel (chapter 5.3)
The way was to use certificates

modifiy connection manager information from SSIS Catalog from sql

Is there a way, to update the connection manager information from the ssis catalog after deploying with an sql code ?
I'd like to deploy the project without sensitive data first :
exec catalog.deploy_project ..
then add username and password to the SSIS Catalog Project via SQL...
Is there a way, to update the connection manager information from the
ssis catalog after deploying with an sql code ?
It is possible since all changed in SSISDB catalog are via stored procedure.
This is a SQL script generated by SSMS during connection string change:
DECLARE #var SQL_VARIANT
= N'Data Source=ServerName;Initial Catalog=dbName;
Provider=SQLNCLI11.1;Integrated Security=SSPI;Auto Translate=False;';
EXEC [SSISDB].[catalog].[set_object_parameter_value] #object_type = 20,
#parameter_name = N'_ConnectionStringParam',
#object_name = N'ProjectName',
#folder_name = N'FolderName',
#project_name = N'ProjectName',
#value_type = V,
#parameter_value = #var;
References:
catalog.set_object_parameter_value (SSISDB Database)
How can I automate setting SSIS Project Parameters in SSIS 2012?
Since you're asking how to change the username and password once a package is deployed, I assume you're already familiar with the SSIS Catalog, if not the documentation outlines this further. To set the username and passwords via T-SQL use environment variables, with the variable holding the password marked as sensitive. The SSISDB.CATALOG.SET_ENVIRONMENT_VARIABLE_VALUE stored procedure is used to update the values of environment variables, including those marked as sensitive, and can be used as follows.
Start off by creating an environment. To do this, right-click SSISDB and create a new folder. Then in this folder, right-click and create a new environment.
After this right-click the environment and select Properties then go to the Variables pane. Create a string variable for both the username and password, with the variable storing the password set as Sensitive.
Next, right click the package or project depending on the scope that the connection manager was defined in, and press Configure and go to the References page. On this add the newly created environment with the Add button.
Still in the properties window of the project/package, go to the Parameters page and then Connection Managers tab. Find the connection manager that will use the environment variables, right-click the ellipsis on both the username and password properties, change the radio button to "Use Environment Variable", and choose the corresponding variables.
Before executing the package, run the SSISDB.CATALOG.SET_ENVIRONMENT_VARIABLE_VALUE stored procedure to update the environment variables. An example of this follows.
If this package is executed using T-SQL from a SQL Agent job or another method, linked the environment reference to it as done below.
Example:
DECLARE #usernameVar SQL_VARIANT = N'UsernameValue'
EXEC SSISDB.[CATALOG].SET_ENVIRONMENT_VARIABLE_VALUE #variable_name=N'Username',
#environment_name=N'Environment Name', #folder_name=N'Environment folder', #value=#usernameVar
DECLARE #passwordVar SQL_VARIANT = N'PasswordValue'
EXEC SSISDB.[CATALOG].SET_ENVIRONMENT_VARIABLE_VALUE #variable_name=N'Password',
#environment_name=N'Environment Name', #folder_name=N'Environment folder', #value=#passwordVar
--make sure environment mapped with #reference_id
DECLARE #execution_id bigint
EXEC SSISDB.[CATALOG].CREATE_EXECUTION #package_name=N'Package.dtsx', #execution_id=#execution_id OUTPUT,
#folder_name=N'Project Folder', #project_name=N'PackageProject', #use32bitruntime=False, #reference_id=99
DECLARE #var0 smallint = 1
EXEC SSISDB.[CATALOG].SET_EXECUTION_PARAMETER_VALUE #execution_id, #object_type=50,
#parameter_name=N'LOGGING_LEVEL', #parameter_value=#var0
--execute package
EXEC SSISDB.[CATALOG].START_EXECUTION #execution_id

"service broker" The server principal "sa" is not able to access the database under the current security context

I'm working with SQL Server 2012 Express.
I'm using Service Broker to run a stored procedure asynchronously.
The activation procedure has to access another database to execute another stored procedure. This is the code:
CREATE PROCEDURE [dbo].[GetNewCodes]
#gintNewCodes bigint,
#presNewCodes tinyint,
#levelNewCodes bigint,
#quantityNewCodes smallint
AS
-- Get new codes from INCIC database.
DECLARE #return_value int,
#xmlGenerated xml,
#xmlString NVARCHAR(MAX)
SET NOCOUNT ON;
-- Set that this stored procedure is running
update dbo.RunningSPs with (serializable) set conf_value = 1
where sp_name = N'GetNewCodes'
if ##rowcount = 0
begin
insert dbo.RunningSPs(sp_name, conf_value) values (N'GetNewCodes', 1)
end
EXEC #return_value = [INCIC].[dbo].[ReadCodeBuffer]
#gint = #gintNewCodes,
#pres = #presNewCodes,
#level = #levelNewCodes,
#quantity = #quantityNewCodes,
#xmlGenerated = #xmlGenerated OUTPUT
SET #xmlString = cast(#xmlGenerated as nvarchar(max))
-- Process these new codes on TRZ.
EXEC dbo.ProcessCodes #XmlString = #xmlString
-- Update that we are not running this procedure any more.
update dbo.RunningSPs with (serializable) set conf_value = 0
where sp_name = N'GetNewCodes'
if ##rowcount = 0
begin
insert dbo.RunningSPs(sp_name, conf_value) values (N'GetNewCodes', 0)
end
The problem is here: [INCIC].[dbo].[ReadCodeBuffer], and the error message is:
Error: 50000
Unrecoverable error in procedure GetNewCodes: 916: The server principal "sa" is not able to access the database "INCIC" under the current security context.
I have followed this tutorial to implement Service, queue and activation stored procedure.
How can I fix this problem?
Read Call a procedure in another database from an activated procedure.
The problem is that activated procedures are run under an EXECUTE AS USER context and as such are subject to database impersonation restrictions (they are sandboxed within the database). Is all explained in Extending Database Impersonation by Using EXECUTE AS.
The solution is to sign your activated procedure and create an user derived from the signing certificate in the target database, and grant this derived user the required permissions. The first link shows a full example.
Remus Rusanu's answer is clearly definitive and correct. (Anyone dealing with Broker Services knows his expertise and invaluable blog.)
I just wanted to document my experience, since google will direct to this question when searching on "The server principal "sa" is not able to access the database..."
In my case, I was call another database from within an activated procedure, but invoking a sql statement directly, not a stored procedure.
Originally, the cross-database call worked fine without a signed certificate and the use of impersonation. Then, after a small syntax change, it started returning the above error message.
Here is what worked without the need for a signed certificate:
IF EXISTS (SELECT name FROM sys.databases WHERE name = N'MyOtherDb')
and here is what provoked the security exception:
IF DB_ID(N'MyOtherDb') IS NOT NULL

Resources