To secure the database interaction, I have created a restricted user who can only execute the stored procedures. He doesn't have any rights to query any table.
This scenario I have implemented very well!. Now the problem is, one of my stored procedure involves dynamic SQL execution, which fails the execution by saying that I don't have the permission to run SELECT query on table X.
To avoid this, I need to provide explicit SELECT permission to such procedures NOT TO USERS.
Please help me!
There is no build in function to give execute to a user.
Create a role with execute permission and add that role to the user.
CREATE ROLE db_executer
GRANT EXECUTE to db_executer
EXEC sp_addrolemember N'db_executer', N'<username>'
MNagel has the correct link for this, but to reiterate, you want to look at using something like:
CREATE PROC dbo.MyProc
WITH EXECUTE AS 'SomeUser'
AS
BEGIN --Proc
--Do Stuff
END --Proc
GO
"Execute As" has some other gotchas along the way. You have to allow others to impersonate the specified user and that user would need the appropriate select permissions to the underlying tables.
If you want to mass grant permissions, you can do that at the Schema level instead of the object level - something like:
GRANT SELECT, EXEC ON Schema::dbo TO MyRole;
I've used that before to greatly simplify a lot of grant statements for our roles. We have very few overrides for the more general roles so this helps quite a bit.
define the stored procedure to be executed as a user with appropriate rights:
http://msdn.microsoft.com/en-us/library/ms188354.aspx
Is this what you want?
USE DatabaseName
GO
-- 1 - db_executestoredprocedures
-- 1a - Create role
CREATE ROLE db_executestoredprocedures
GO
-- 1b - Grant permissions
GRANT EXECUTE TO db_executestoredprocedures
GO
-- 2 - db_selecttablevaluedfunctions
-- 2a - Create role
CREATE ROLE db_selecttablevaluedfunctions
GO
-- 2 - Create permissions
DECLARE #Function_Name nvarchar(250);
DECLARE #CMDEXEC1 nvarchar(2000);
DECLARE db_cursor CURSOR FOR
SELECT [name]
FROM sys.objects
WHERE Type = 'TF'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #Function_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #CMDEXEC1 = 'GRANT SELECT ON [' + #Function_Name + '] TO db_selecttablevaluedfunctions;'
--- SELECT #CMDEXEC1
EXEC(#CMDEXEC1)
FETCH NEXT FROM db_cursor INTO #Function_Name
END
CLOSE db_cursor
DEALLOCATE db_cursor
GO
On the SP where sql is complaining about you not having the permissions to run a SELECT query on table X, do you have a broken chain of ownership on that particular SP and table?
Related
I have an issues in that when i try to execute a stored procedure through my web application, i am shown the following error
SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user 'elitecareers_admin'#'%' for table 'user'
Here is my stored procedure just for clarity
DELIMITER $$
CREATE PROCEDURE `sp_create_account`( username_param VARCHAR(40), email_param VARCHAR(60), pass_w VARCHAR(30), category_id TINYINT )
BEGIN
DECLARE salt VARCHAR(60);
DECLARE password_var VARCHAR(128);
SET salt = 'ELiCrs#4$^7EC%?';
SET salt = CONCAT( username_param, salt );
SET password_var = SHA2( CONCAT( pass_w, salt ), 0 );
INSERT INTO elite.user
( user_id, username, email, pass, active, date_joined, user_category_id )
VALUES
( DEFAULT, username_param, email_param, password_var, DEFAULT, DEFAULT, category_id );
END $$
DELIMITER ;
and here is how the stored procedure looks when i run the show command
Here is my server information too
Here are the privileges for the user 'elitecareers_admin'#'%' under which the web application is executing
What i don't understand if the user has all privileges on the database, then why does mysql/maria server keeping those errors?
"The SQL SECURITY characteristic can be DEFINER or INVOKER to specify the security context; that is, whether the routine executes using the privileges of the account named in the routine DEFINER clause or the user who invokes it. This account must have permission to access the database with which the routine is associated. The default value is DEFINER. The user who invokes the routine must have the EXECUTE privilege for it, as must the DEFINER account if the routine executes in definer security context. " -- https://dev.mysql.com/doc/refman/5.7/en/create-procedure.html
So, regardless of who runs the proc, the permissions for 'elitecareers_admin'#'%' will be used for things such as accessing the table elite.user. But that user seems to have privileges only to the database elitecareers\_elite.
Plan A: Recreate the proc with SQL SECURITY INVOKER if that is appropriate.
Plan B: Figure out the difference between databases elite and elitecareers\_elite.
Plan C: Add another GRANT to let that admin get to elite.
Plan D: (There are probably other solutions.)
How can admin prevent himself to drop or truncate specific tables, because sometimes a table is accidently truncated or deleted, making view is not a good idea. if i make a trigger ,it is implemented in all tables in db. i just want to implement it in specific tables, in sql server ?
Create TRIGGER [TR_ProtectCriticalTables]
ON DATABASE
FOR
DROP_TABLE
AS
DECLARE #eventData XML,
#uname NVARCHAR(50),
#oname NVARCHAR(100),
#otext VARCHAR(MAX),
#etype NVARCHAR(100),
#edate DATETIME
SET #eventData = eventdata()
SELECT
#edate=GETDATE(),
#uname=#eventData.value('data(/EVENT_INSTANCE/UserName)[1]', 'SYSNAME'),
#oname=#eventData.value('data(/EVENT_INSTANCE/ObjectName)[1]', 'SYSNAME'),
#otext=#eventData.value('data(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]',
'VARCHAR(MAX)'),
#etype=#eventData.value('data(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)')
IF #oname IN ('tbluser')-- You can give comma seperated list here
BEGIN
DECLARE #err varchar(100)
SET #err = 'Table ' + #oname + ' is super duper protected and cannot be dropped.'
RAISERROR (#err, 16, 1) ;
ROLLBACK;
END
GO
ENABLE TRIGGER [TR_ProtectCriticalTables] ON DATABASE
For disabling the truncate try this
EXEC sys.sp_cdc_enable_table
#source_schema = N'dbo',
#source_name = N'TestTable',
#role_name = NULL
GO
This may cause some other problems so please check before using.
Just remove the permissions dir that special admins. And create a second user which has the permissions for doing such dangerous jobs.
See also the GRANT and REVOKE SQL commands.
Please create user and create trigger for superadmin who has all permissions like :
CREATE TRIGGER reminder2
ON Customer
with execute as owner
AFTER DELETE
AS
truncate table Customer
Problem
I have a stored procedure:
CREATE PROCEDURE `ProblematicProcedure` (IN dbName varchar(50), IN tableId INT)
MODIFIES SQL DATA
BEGIN
DROP VIEW IF EXISTS v1;
DROP VIEW IF EXISTS v2;
CALL ExecuteSql(CONCAT("CREATE VIEW v1 AS SELECT * FROM ",dbName,".my_table;"));
CALL ExecuteSql(CONCAT("CREATE VIEW v2 AS SELECT * FROM ",dbName,".table_",tableId,";"));
...
When called directly from command line or a client like Navicat or HeidiSql, it works well:
CALL ProblematicProcedure("my_schema",1);
But if called from a custom Apache module using the exactly same line above, it crashes on first ExecuteSql call. I have to make it work when called from the Apache module and couldn't find a reason to crash.
ExecuteSql definition
CREATE PROCEDURE ExecuteSql (IN sql_str TEXT)
BEGIN
SET #query = sql_str;
PREPARE stm FROM #query;
EXECUTE stm;
DEALLOCATE PREPARE stm;
END
What I tried?
Swapped two ExecuteSql calls.
Inlined ExecuteSql calls.
Removed ExecuteSql's and used direct SQL statements with hardcoded dbName and tableId values.
Created procedure without MODIFIES SQL DATA.
Granted CREATE VIEW privilege: GRANT ALL ON *.* TO 'myuser'#'%';
Note: I added simple insert statements between the lines to find where it is crashing. So, I am sure it crashes always on first ExecuteSql call.
Question
What can be reason to this crash?
Update: Finally, I managed to find error code:
ERROR 1312: Procedure can't return a result set in the given context
Solution
Use CLIENT_MULTI_STATEMENTS flag when connecting:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
Why this is so?
Calling a stored procedure means executing multiple statements. So, I need to specify that I can execute multiple statements at once. Hence I am using MySql C API functions at client-side (in my Apache module), I need to specify CLIENT_MULTI_STATEMENTS flag when connecting:
mysql_real_connect(conn, host, user, pass, db, 0, NULL, CLIENT_MULTI_STATEMENTS);
Or set it later:
mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON);
I learnt those from the C API Handling of Multiple Statement Execution page.
How I Debugged?
Debugging a stored procedure is not so easy. I used traditional log-table method, but performed a bit aggresively about finding the error code.
Firstly, defined two variables to keep the code and message about the error occurred:
DECLARE E INT DEFAULT 0; -- error code
DECLARE M TEXT DEFAULT NULL; -- error message
Then, defined possible error codes and messages both for client and server errors (full list here):
DECLARE CONTINUE HANDLER FOR 1000 SET E='1000', M="hashchk";
DECLARE CONTINUE HANDLER FOR 1001 SET E='1001', M="isamchk";
...
...
DECLARE CONTINUE HANDLER FOR 1312 SET E='1312', M="PROCEDURE %s can't return a result set in the given context";
...
...
DECLARE CONTINUE HANDLER FOR 1638 SET E='1638', M="Non-ASCII separator arguments are not fully supported";
DECLARE CONTINUE HANDLER FOR 1639 SET E='1639', M="debug sync point wait timed out";
DECLARE CONTINUE HANDLER FOR 1640 SET E='1640', M="debug sync point hit limit reached";
...
...
DECLARE CONTINUE HANDLER FOR 2057 SET E='2057', M="The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again";
And finally, put logs in critical points:
IF E> 0 THEN
CALL WriteLog(CONCAT("Error ", E, ": ", M));
END IF;
WriteLog is another procedure that only inserts into a log table. This method gave me the error code (1312) and then some Googling worked.
I've created a SQL agent job in our SQL Server 2008 that executes the following code:
BEGIN
SET NOCOUNT ON;
declare #database nchar(20);
DECLARE Database_Cursor CURSOR FOR
SELECT [name]
FROM master.dbo.sysdatabases
ORDER BY [name]
OPEN Database_Cursor;
FETCH NEXT FROM Database_Cursor INTO #database;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [dbo].[sp_BackupDatabase]
#databaseName = #database
FETCH NEXT FROM Database_Cursor INTO #database;
END;
CLOSE Database_Cursor;
DEALLOCATE Database_Cursor;
END
GO
Basically I retrieve a list of databases and execute a backup script for each database.
This script works for most database, but some databases return an error:
Msg 911, Level 16, State 11, Line 1
Database 'appName_Configuration' does not exist. Make sure that the name is entered correctly.
This is correct, this database does not exist. We have several database named like this:
appName_Configuration1
appName_Configuration2
...
For some reason, the script doesn't pass the name including the number to the backup script. If I replace the #database variable with a static name (appName_Configuration1) it works just fine.
Also worth mentioning: there are a few other databases that have a number at the end, which work fine:
appName_Microsoft1
appName_Microsoft2
I suspect that the word "Configuration" may have something to do with it, but renaming the database is not an option for now. Can anyone help me finding a solution so that the name is passed to the stored procedure correctly ?
Kind regards,
Mathew
Increase the size of #database variable as per your database name maximum length can be...
e.g.
declare #database nchar(100);
You declared #database as nchar(20). Your names are longer. Use nvarchar(50), for example.
I'm using postgresql 8.4 and am having a bit of a problem granting select privileges on a view from a database onwed by a different user when the view uses any of the databases functions.
As the new user, when I try to run, for example select * from users_pwd; where users_pwd is defined as:
create view users_pwd as
select *, get_pwd(id)
from users;
and get_pwd as:
CREATE OR REPLACE FUNCTION get_pwd(p_id integer)
RETURNS text AS
$BODY$
declare u record;
BEGIN
select into u * from users where id = p_id;
return u.password;
END;
$BODY$
LANGUAGE plpgsql;
I get the following error:
ERROR: permission denied for relation users
CONTEXT: SQL statement "select * from users where id = $1 "
PL/pgSQL function "get_pwd" line 3 at SQL statement
The only way to have the user query the view is to explicitly grant select on the table users which I don't want to do.
If a view doesn't use any function, but rather just other tables which the new user doesn't have explicit access to it works perfectly fine.
You could create the function with owner who can select from the table users. Such a function should be created with SECURITY DEFINER clause, so it will be executed with the owner rights.
More information you can find here: http://www.postgresql.org/docs/9.0/interactive/sql-createfunction.html
You can also GRANT EXECUTE privileges on functions. See GRANT in the docs.