Restrict insert permissions on a single table in MySQL - mysql

I have a MySQL table that needs restricted permissions for INSERT.
Specifically, I have written a stored function that handles all INSERT operations, and I need to restrict permissions so ONLY this stored function is allowed to insert rows in this specific table.
"But why would you do that?"
Because MySQL doesn't support Microsoft's INSTEAD OF INSERT triggers, and a custom stored function/procedure for insertion is the only viable workaround.
And I need it to work like a trigger - guaranteed to execute for every INSERT operation, with no exceptions or loopholes.
The function returns 0 if no errors, 1 if invalid parameters, 2 if unique index collision, etc.
(Or maybe the function will include an INSERT ... ON DUPLICATE KEY UPDATE statement, I haven't decided yet.)
"But why do you need INSTEAD OF INSERT triggers?"
For the same reason that Microsoft SQL Server developers need it ... because it does the job that needs to be done ...
Specifically, because my code includes GET_LOCK before insertion, and RELEASE_LOCK after insertion ... and I don't want a BEFORE INSERT trigger to end without releasing the lock (seems like a very bad idea).

You can create MySQL stored functions (and stored procedures) including the clause SQL SECURITY DEFINER . Then, they'll run with the permissions of the definer, not the user. Your definition might look like this:
CREATE PROCEDURE proc()
SQL SECURITY DEFINER
BEGIN
INSERT INTO tbl whatever....;
END;
When you do this, log in to MySQL with an administrator's account, not a user's account.
Then use MySQL's permission system to grant INSERT access for that table to the administrator's account and revoke it for other users' accounts.
GRANT SELECT, UPDATE, INSERT, DELETE mydatabase.mytable TO admin;
REVOKE INSERT ON mydatabase.mytable FROM user1;
REVOKE INSERT ON mydatabase.mytable FROM user2;
Then, INSERT queries from other users' accounts will fail, but the stored function will succeed. This is more-or-less conventional SQL stored code privilege handling.
MySQL triggers, of any flavor, can't start or end transactions.

Related

SQL Trigger on database to restrict drop/alter/create syntax error, despite following tutorial, ON is not valid at this position?

Working on a security DDL trigger for a database and want to restrict non-admins from creating/dropping database, I have created this trigger and put it below all the other code (create tables etc) in the database script.
CREATE TRIGGER alert_table
ON DATABASE
FOR CREATE_TABLE, DROP_TABLE, ALTER_TABLE
AS
BEGIN
IF IS_MEMBER ('admin') = 0
BEGIN
PRINT 'Please contact your Database Admin'
ROLLBACK TRANSACTION;
END
END
GO
CREATE
Note:
USER 'admin'#'localhost'
It says "ON is not valid at that position, expecting: BEFORE or AFTER", and something similar for "END". Thanks!
You followed a tutorial for Microsoft SQL Server, commonly abbreviated as mssql server, while you are using mysql.
Mssql server has DDL triggers which syntax follows the one in your question.
Mysql's create trigger syntax is different and does not have DDL triggers at all, so you cannot use triggers to handle table creation events.

can i generate a mysql trigger without a definer clause?

Question is dead simple. I want to add a trigger to my database, any simple one like:
CREATE TRIGGER before_user_update BEFORE UPDATE ON `users`
FOR EACH ROW
BEGIN
SET NEW.user_id = NEW.old_user_id_column;
END
Unfortunately when i do that i always get a DEFINER clause in the dump with host and username, which is very troublesome because i need to use this on different environments on which this definer will be completely different?
Is there any workaround?
I guess this question could be asked: how to use the same query trigger in multiple environments?
(im using rails)
I know i can just remove the definer part from the schema manually, but it will cause problems to my coworkers who are going to run a migration and still get the clause in their schema
No, you cannot create a trigger without the definer clause.
As mysql documentation on create trigger says:
If the DEFINER clause is omitted, the default definer is the user who executes the CREATE TRIGGER statement. This is the same as specifying DEFINER = CURRENT_USER explicitly.
So, even if you omit the definer, mysql will give you one. So, you either create the same mysql user across the different environments or you save the trigger code with DEFINER = CURRENT_USER. Unfortunately, when you run the create trigger code, mysql will substitute the current user's name into the definer clause.

How to update a table from a trigger, when column name is dynamic?

Schema and data for test database - https://gist.github.com/koceg/435c0d2b1246a69d048f
My goal is to update boards table, when somebody inserts a new row in the objects_properties table. The name of the column to update is dynamic - it depends on a property_id from objects_properties.
So far I've created a trigger and a stored procedure, but I'm getting this error:
Dynamic sql is not allowed in stored function or trigger.
Am I doing something wrong or is mysql not allowing to call a stored procedure with a prepared statement inside a trigger? If so, how can I do what I want?
I have an idea, but it's ugly even in a pseudocode. Real SQL will be even worse, because there will be a few dozens codes:
SWITCH (property_code)
CASE 'name'
INSERT INTO boards (id, name) VALUES (#object_id, #value) ON DUPLICATE KEY UPDATE name = #value;
CASE 'address'
INSERT INTO boards (id, address) VALUES (#object_id, #value) ON DUPLICATE KEY UPDATE address = #value;
CASE 'district'
INSERT INTO boards (id, district) VALUES (#object_id, #value) ON DUPLICATE KEY UPDATE district = #value;
P.S. I can't move this logic to my application, because this database is used by several applications.
The Current MySQL (5.7) Manual section D.1 Restrictions on Stored Programs states that
SQL prepared statements (PREPARE, EXECUTE, DEALLOCATE PREPARE) can be used in stored procedures, but not stored functions or triggers. Thus, stored functions and triggers cannot use dynamic SQL (where you construct statements as strings and then execute them).
Generally, statements not permitted in SQL prepared statements are also not permitted in stored programs. For a list of statements supported as prepared statements .
Because local variables are in scope only during stored program execution, references to them are not permitted in prepared statements created within a stored program. Prepared statement scope is the current session, not the stored program, so the statement could be executed after the program ends, at which point the variables would no longer be in scope.
So you can see that it's not permitted.
Regards.

Mysql limit insert/update/delete to being called from trigger (Not from application code)

My use case is that I have a MYSQL table which has a change tracking table behind it. Both tables are InnoDB.
I'd like to use triggers to enforce two things.
One: forcing a copy to be made of previous state on every update (update trigger performs insert select query) This is non-trivial and I have already done it.
Two: limit access to the second table to read only for users, while triggers can still insert/update/delete as necessary
Further research on user access and triggers has dug up this:
Mysql 5.5 reference manual: 19.6 Access Control for Stored Programs and Views
Turns out that stored programs can be assigned a user to run as. Triggers are stored programs. By restricting Insert, Update, and Delete to a special user which only will be used by a trigger and using the DEFINER attribute of the trigger (or any stored program) I can make the trigger which I want to have access run under that user.

creating a trigger to lock and unlock a record

I am creating a php script for a MySQL database whereby I call a MySQL trigger..
The trigger should affect a table which is effectively an invoice:
So when I update a field called 'date_invoiced' from its NULL default to a valid date it then locks the whole record from being updated unless you have permission via your MySQL logon to change it back to its default NULL, (effectively 're-opening' the invoice)
No idea how to do this, any help would be great
You can't put a lock on a row. I suggest you use a TRIGGER on update, which makes the update fail if date_invoiced is NOT NULL. Unless username is 'superman'.
I think that you can code what you want following this example.
DELIMITER ||
CREATE TRIGGER upd_lock
BEFORE UPDATE
ON table_name
FOR EACH ROW
BEGIN
IF OLD.date_invoiced IS NOT NULL AND USER() NOT LIKE '\'superman\'#%' THEN
SIGNAL SQLSTATE VALUE '45000' SET MESSAGE_TEXT = '[upd_lock] - Record is locked';
END IF;
END;
||
DELIMITER ;
Adding triggers is essential for the development of complex MySQL databases that retain enforced referential integrity. Foreign keys cannot handle complex cases that perhaps involve more than one column (such as an item_id and item_type_id scenario).
SUPER is required when creating or dropping trigger only when binary logging is turned on.
The reason appears to be related to replication issues (MySQL 5.0 documentation).
RTM.and RTM
Read this link to ... & this threads Applying column permissions for a table over a trigger , Can't create MySQL trigger with TRIGGER privilege on 5.1.32