<b>admin </b> prevent himself to drop specific tables? - sql-server-2008

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

Related

Unable to create a trigger in mysql hosted on amazon cloud

When am trying to create a simple trigger in mysql, am encountering the below error message. Please suggest me how to overcome this.
delimiter $$
create trigger trg_addresses_ins before insert on addresses
for each row
begin
declare msg varchar(128);
if length(new.addressstate) > 2 then
set msg = concat('MyTriggerError: Trying to insert a state value of more than 2 character: ', new.addressstate);
signal sqlstate '45000' set message_text = msg;
end if;
end$$
delimiter ;
`
Error Code: 1419. You do not have the SUPER privilege and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable) 0.078 sec
Super user is enabled but still get the same error and also am unable to change database parameter group associated with mysql aws db instance to 1. I am unable to modify db instance to select newly created group as the parameter group field is read only.
Appreciate your valuable inputs.
Thanks!
I guess you are using the default DB parameter group which you can not modify, the solution is you need to create your own parameter group, and set log_bin_trust_function_creators = 1, and apply your own parameter group to your current instance.

Syntax error MySql trigger: While_Sym

I'am developing a small project with PHP and MySql on a Wamp server. I just discovered the wonderful principle of SQL triggers. Hum... well. It would be wonderful if I could use it.
Indeed I have a problem with the following script:
BEGIN
SET #liste = NEW.reponse
WHILE LEN(#liste) > 0
BEGIN
IF PATINDEX('%,%',#liste) > 0
BEGIN
SET #choix = SUBSTRING(#liste, 0, PATINDEX('%,%', &liste))
INSERT INTO resultat (referendum, choix) VALUES (NEW.id, #choix)
SET #liste = SUBSTRING(#liste, LEN(#choix + ',') + 1, LEN(#liste))
END
END
END
I would like to execute this trigger after the insertion of a record in table "Referendum". In this table, there is a field "reponse" which contains the different possible answers. This field contains this kind of data: "Yes,No,I don't know". For each new question, I want to insert a new record in table "Resultat" per possible answer.
In my example, three new records: one for Yes, one for No and one for I don't know.
My code comes from an example on the internet but it doesn't work properly. SQL returns a syntax error with message "While_Sym expected"...
I tried to add semicolon following what I found on the internet but no way...
I guess you need something like this:
CREATE TRIGGER mytrigger AFTER INSERT
ON Referendum FOR EACH ROW
BEGIN
DECLARE cnt int;
DECLARE str varchar(100);
Set cnt = CHAR_LENGTH(NEW.reponse)
- CHAR_LENGTH(Replace(NEW.reponse,',','')) +1;
WHILE cnt > 0 DO
Set str = SUBSTRING_INDEX(
SUBSTRING_INDEX( New.reponse,',', -cnt)
,',',1);
INSERT INTO resultat (referendum, choix)
VALUES (NEW.id, str);
Set cnt = cnt - 1;
END WHILE;
END;
Demo: http://sqlfiddle.com/#!2/c7321/1
Some thoughts:
There are no PATINDEX nor LEN functions in MySql, they come from SQL Server.
Most functions are not standard in SQL, one shouldn't expect that something that works on database X should also work on database Y (and vice versa)
You always need to check the manual.
There is difference in MySql between #variable and variable - they are not the same (opposite to SQL Server where there is only one kind of variables --> #variable).
Please refer to documentation to learn about #user_definied_variables and local_variables
http://dev.mysql.com/doc/refman/5.7/en/user-variables.html
http://dev.mysql.com/doc/refman/5.7/en/declare-local-variable.html
Depending on your client software you may need to use also DELIMITER xx command, for example in mysql console client or MySql-Workbench you need something like this to create a trigger without syntax errors:
DELIMITER $$
CREATE TRIGGER mytrigger AFTER INSERT
ON Referend ......
......
......
END;
$$
DELIMITER ;

How to GRANT SELECT permission to all procedures

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?

Is there a method in MySQL to protect fields from changes?

We get this daily data feed. (We have no control over the original data, so asking them to correct the database isn't an option.)
The customer records contain addresses in the US. Street address, City, State and Zip.
On our end, we use the data in a database for the marketing department. They sometimes find the address is incorrect, or incomplete, and want to make changes to it. But of course, the next data feed would come in and wipe out their corrections.
Is there a method inf MySQL to protect certain fields from being changes, kind of like a protected cell in a spreadsheet. This is some of the field names of the MySQL record layout:
address1 address2 address3 city state zipcode
What if I created along side of this, additional fields that are flagged either "Y" or "N" as being a protected field:
address1 address1_flag address2 address2_flag
address3 address3_flag city city_flag state
state_flag zipcode zipcode_flag
So when the marketing department corrects, for example, the zipcode, it would set the zipcode_flag to "Y" meaning, Yes protect the field zipcode from further changes. If the original data feed does get corrected at a later point, then if zipcode from the marketing department's database matches the original field, then the zipcode_flag protection would be changed to "N".
Does this sound like the correct method to manage the marketing department's database from the daily feed? Or is there another approach or feature available in MySQL to do this? Thanks!
I don't think there's a "protected" flag or feature, but there are a few roads you could take to accomplish your goal.
The first and most specific would be to create a "restricted user" in MySQL. To restrict the user, you can/would grant only SELECT privileges to the column(s) you don't want modified. To do this you would use:
GRANT SELECT(zipcode) ON addresses TO restrictedUser;
You can see a good example of this here, or get detailed information in the manual.
A second method would be to create a procedure that selects/inserts/updates. This one may be overkill, but could be accomplished to suit your needs and won't require modifying user permissions.
A simple example of a select and update procedure would be (not tested):
CREATE PROCEDURE select_addresses ()
BEGIN
SELECT address1, zipcode FROM addresses;
END
CREATE PROCEDURE update_addresses ( IN recordID INT(11), IN newAddress1 VARCHAR(255) )
BEGIN
SET #query := CONCAT("UPDATE addresses SET address1 = '", newAddress1, "' WHERE id = ", recordId);
PREPARE stmt FROM #query;
EXECUTE stmt;
END
This will allow a user to select any column you specify that they're allowed to read by calling select_addresses() and then perform an update on any allowed column via update_addresses(). You'd have to add several layers of logic to only update variables that have been set, etc - so using a procedure may in fact be overkill =P
You can manage privileges at the column-level: http://dev.mysql.com/doc/refman/5.1/en/grant.html.
Another approach would be to set a trigger that works off a flag in the records. For example if you had a field in the table
modified TINYINT(1) NOT NULL DEFAULT 0
THEN you create a Trigger to preserve the data
CREATE DEFINER = CURRENT_USER TRIGGER `database_name`.`table_name_BEFORE_UPDATE` BEFORE UPDATE ON `table_name` FOR EACH ROW
BEGIN
IF (NEW.modified=1) THEN
/* Then set any fields you want to preserve to their old value */
SET NEW.address1=OLD.address1;
SET NEW.address2=OLD.address2;
SET NEW.city=OLD.city;
SET NEW.stateprov=OLD.stateprov;
SET NEW.postal_code=OLD.postal_code;
SET NEW.country=OLD.country;
END IF;
END
This would allow you to restrict modifications unless modified is set to 1. If you wanted to take it one step further and once modified prevent it from ever being modified, the modified flag could also be included so once set it couldn't change short of pulling the trigger from the db
CREATE DEFINER = CURRENT_USER TRIGGER `database_name`.`table_name_BEFORE_UPDATE` BEFORE UPDATE ON `table_name` FOR EACH ROW
BEGIN
IF (NEW.modified=1) THEN
/* Then set any fields you want to preserve to their old value */
SET NEW.modified=OLD.modified;
SET NEW.address1=OLD.address1;
SET NEW.address2=OLD.address2;
SET NEW.city=OLD.city;
SET NEW.stateprov=OLD.stateprov;
SET NEW.postal_code=OLD.postal_code;
SET NEW.country=OLD.country;
END IF;
END

Replacement of Context_Info SQL Azure

I am currently using the CONTEXT_INFO property of the Master database for storing the logged in username to use it later in Table Triggers for auditing.
While migrating to SQL Azure, the issue of Cross-Database connections popped and I couldn't find direct solutions to this issue.
Following are the Issue Details:
I call Stored Procedure XXX from Data Access Layer and pass the Username as Parameter
The username is used to set the CONTEXT_INFO value in XXX
The CONTEXT_INFO value is then used in Tables Insert/Update/Delete Triggers to Store Username for Application Auditing
Solutions that I found so far:
Create Table In Database to work as CONTEXT_INFO
Use 2 Connection Strings in Data Access Layer, one for Master Database (to set CONTEXT_INFO) and the other is for the application and execute the SET CONTEXT_INFO each time before opening the connection to my application
But I find both solutions risky, specially when expanding the Database over multiple SQL Azure Databases in the future.
Appreciate your support.
The approach I took is shown below. On trick was to check to see running not running on SQL Azure, then we would need to call 'SET CONTEXT_INFO ...'. This allows the same code to be execute on local SQL Server Express and Azure without changes.
Create a table to store the context info (not in master but in the same database)
CREATE TABLE [dbo].[ContextInfo] (
[ContextInfo] varbinary(128) not null,
[ApplicationUsername] nvarchar(128) not null,
[UpdatedAt] datetime NOT NULL,
CONSTRAINT [PK_UserContextInfo] PRIMARY KEY CLUSTERED ([ContextInfo] ASC)
)
Create a stored procedure to 'Set Context Info' which is called from application
CREATE PROCEDURE [dbo].[SetContextInfo]
#ApplicationUsername nvarchar(128)
AS
SET NOCOUNT ON
-- Remove all context items older than an 5 minutes ago
DELETE
FROM [dbo].[ContextInfo]
WHERE [UpdatedAt] < DATEADD(mi, -5, GETUTCDATE())
--
-- Use the MERGE command to do an update/insert
-- See: http://technet.microsoft.com/en-us/library/bb510625.aspx
--
IF SERVERPROPERTY('edition') <> 'SQL Azure'
BEGIN
DECLARE #b varbinary(128)
SET #b = CONVERT(varbinary(128),newid())
EXEC sp_executesql #statement=N'SET CONTEXT_INFO #b',#params=N'#b varbinary(128)',#b=#b
END
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
MERGE [dbo].[ContextInfo] AS target
USING (SELECT #ContextInfo, #ApplicationUsername) AS source ([ContextInfo], [ApplicationUsername])
ON (target.[ContextInfo] = source.[ContextInfo])
WHEN MATCHED THEN
UPDATE SET [ApplicationUsername] = source.[ApplicationUsername], [UpdatedAt] = GETUTCDATE()
WHEN NOT MATCHED THEN
INSERT ([ContextInfo], [ApplicationUsername], [UpdatedAt])
VALUES (source.[ContextInfo], source.[ApplicationUsername], GETUTCDATE());
Create a stored procedure to 'Get Context Info'
CREATE PROCEDURE [dbo].[GetContextInfo]
AS
SET NOCOUNT ON
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
SELECT [ApplicationUsername]
FROM [dbo].[ContextInfo]
WHERE [ContextInfo] = #ContextInfo
GO
In trigger source, use:
DECLARE #UserContext TABLE ([Username] VARCHAR(128))
INSERT INTO #UserContext (Username)
EXEC [dbo].[GetContextInfo]
Now you have the username stored in the table variable. In case changes are applied by an administrator outside of your application, you may also want to check if the username was not set and default to something like *SYSTEM_USER*.