Hello I have a table on which I have denied SELECT privs to a user.
This table has a trigger which references the INSERTED table, basically doing an
AFTER UPDATE SET <table>.[UPDATED] = getdate()
WHERE ROWID IN SELECT ROWID FROM INSERTED
It is giving me an error though, saying "SELECT PERMISSIONS DENIED", I am guessing because of the SELECT FROM INSERTED.
How can I keep the SELECT deny, but allow the trigger to SELECT from the INSERTED pseudotable?
Thanks in advance!
Consider adding an EXECUTE AS clause so the trigger runs with the schema owner's permissions.
CREATE TRIGGER [dbo].[TR_Product_Update] ON [Product]
WITH EXECUTE AS OWNER
AFTER UPDATE
AS
SELECT ProductId
FROM INSERTED
Why did you deny select? What about just not granting select to them? There is a subtle difference between denying select and just not granting it to them. Also, if you denied select to any of the system level roles, then that would also probably be part of the problem.
--EDIT--
In the comments you asked whether or not SQL Server has context info. 2005 does and you can see how to use it here.
Session Variable – Context_Info: Session is a powerful tool in any of the programming language. SQL-Server is not a full fledge programming language but it do supports session variable for current session or connection. It stores value of session in 128 byte of binary information.
join to the inserted table instead something like:
update t1
set updated = getdate()
from table1 t1
join inserted i
on i.rowid = t1.rowid
This will probaly also perfom better than a subselect anyway.
I suspect your problem is that your UPDATE statement itself requires the SELECT permission.
I created a test database as follows:
DROP DATABASE triggerPermissionTest
CREATE DATABASE triggerPermissionTest
GO
USE triggerPermissionTest
GO
CREATE USER foo FROM LOGIN tester
GO
CREATE TABLE triggerTable (id int)
GO
DENY SELECT ON triggerTable to foo
GRANT UPDATE ON triggerTable to foo
GO
CREATE TRIGGER execAsTrigger ON triggerTable
AFTER UPDATE AS SELECT * FROM triggerTable
GO
INSERT INTO triggerTable VALUES (1)
GO
and tried the following Update statements with the 'tester' login:
UPDATE triggerTable SET id = 2
GO
UPDATE triggerTable SET id = id *2
GO
The first one executes fine and the trigger executes (providing the results of select * from triggerTable to the user without select permissions, demonstrating that it managed to do a select).
The second one gives me an error stating that I don't have select permission on triggerTable (because it needs to select from triggerTable in order to get the value of id to do the update).
This leads me to conclude that the trigger is incidental to the problem, and the UPDATE statement is the cause of the permission issue.
Related
So I have a table with all of the reports that our in-house site has. I am trying to make a trigger so that when a new report is added to this list that all users get updated with the permission. (Default access False)
I don't full understand Triggers and have been performing this task with a couple of sql statements in conjunction with C#. Though I'm certain there is a way to do this with just an SQL Trigger.
This is the closest I have gotten thus far (Not Functioning). Names altered for Security.
CREATE TRIGGER [New Trigger]
ON [dbo].[List_Table]
FOR INSERT
AS
BEGIN
INSERT INTO [dbo].[Permission_Table]
(UserId, ReportId, Granted)
(SELECT UserId FROM [dbo].[User_Table]),(SELECT ReportId FROM inserted),false
END
The goal is for it to add a row in the permission table for each user with the list table id and the value of false.
I think you just want an insert . . . select:
CREATE TRIGGER [New Trigger]
ON [dbo].[List_Table]
FOR INSERT
AS
BEGIN
INSERT INTO [dbo].[Permission_Table](UserId, ReportId, Granted)
SELECT u.UserId, i.ReportId, 'false'
FROM Inserted i CROSS JOIN
[dbo].[User_Table] u;
END
I am creating a security layer on few databases. As part of that I have to create a view in Original_Database that joins two tables (union) from different databases, Chained_Database1 and Chained_Database2.
USE Original_Database;
GO
CREATE VIEW QueryATable
AS
SELECT * FROM Chained_Database1.dbo.ATable
UNION
SELECT * FROM Chained_Database2.dbo.ATable;
GO
If user usergroupA (Usergroup) logs in and selects from the view QueryATable from Original_Database then he should be able to see only data from Chained_Database1.dbo.ATable.
Likewise if usergroupB logs in and selects from the view QueryATable, then he should be able to see only data from Chained_Database2.dbo.ATable.
I have tried giving different permissions to the users. It's either denying view access or giving result from two tables.
Your help is highly appreciated.
Thanks in advance!
Based on your requirements, the UNION is pointless and would only slow performance since you would select from both data sets... and then explicitly limit it to one. Theoretically you'd have a look up table for the user who ran the query, and you'd limit the results in the WHERE clause since you can't user parameters in a view. So, don't do the union. Instead create two separate views.
CREATE VIEW QueryATable
AS
SELECT * FROM Chained_Database1.dbo.ATable
GRANT SELECT ON QueryATable TO <who ever...>
GO
CREATE VIEW QueryBTable
AS
SELECT * FROM Chained_Database2.dbo.ATable
GRANT SELECT ON QueryBTable TO <who ever...>
GO
If you don't want to create separate views then you could handle this in a STORED PROCEDURE or some other way perhaps...
CREATE PROCEDURE yourProcedure(#userID)
AS
IF 1 = (SELECT COUNT(*) FROM LookUpTableA WHERE userID = #userID)
SELECT * FROM Chained_Database1.dbo.ATable
IF 1 = (SELECT COUNT(*) FROM LookUpTableB WHERE userID = #userID)
SELECT * FROM Chained_Database2.dbo.ATable
ELSE
SELECT 'User Not Valid'
GO
Note that this assumes the user would only be in one of the look up tables. If they were in both you'd need to handle it differently.
If I have the following view:
CREATE VIEW [dbo].[vw_Sample]
AS
SELECT T1.Column1,
T1.Column2,
Table2.Column1,
Table2.Column2
FROM dbo.Table1 T1
JOIN dbo.Table2 T2 ON T2.Column3 = T1.Column3
GO
And I want to grant a user access to SELECT from that view; do I need to either:
A - Give that user SELECT permissions on just the view:
GRANT SELECT ON [dbo].[vw_Sample] TO [MyUser]
or
B - Give that user SELECT permissions on view and underlying tables:
GRANT SELECT ON [dbo].[Table1] TO [MyUser]
GRANT SELECT ON [dbo].[Table2] TO [MyUser]
GRANT SELECT ON [dbo].[vw_Sample] TO [MyUser]
Just the View (A). Granting Select on the underlying tables will give them more access than they may need. The link below has some helpful info on ownership of the view\table and the affects it may have on your access.
Grant SELECT permission on a view, but not on underlying objects
B. Each object you created (view, procedure or function) will be execute under caller permissions. So, trying to select from underlying tables without necessary permissions will cause permission error. Or you may use EXECUTE AS/SET USER statements: please refer to TechNet article: http://technet.microsoft.com/en-au/library/ms188268(v=sql.105).aspx
When releasing database code to non-development databases , I use such approach - I create release sqlplus script that runs multiple create table/view/sequence/package/etc statements in a sequence. I also should create rollback script which performs drop and other statements if would be needed during deployment or further use. But it is quite annoying always to create rollback scripts manually. I.E. - when I put
alter table table_a add column some_column number(5);
into release script. I have to put
alter table table_a drop column some_column;
into the rollback script. And vice-versa.
Is there way to optimize(or semi-optimize) it? Maybe some there are some Java/Python/etc libraries that allow to parse ddl statements into logical parts?
Maybe there are some better approaches for release/rollback pl/sql code?
DBMS_METADATA_DIFF and a few metadata queries can automate this process.
This example demonstrates 6 types of changes: 1) adding a column 2) incrementing a sequence 3) dropping a table 4) creating a table 5) changing a view 6) allocating an extent.
create table user1.add_column(id number);
create table user2.add_column(id number);
alter table user2.add_column add some_column number(5);
create sequence user1.increment_sequence nocache;
select user1.increment_sequence.nextval from dual;
select user1.increment_sequence.nextval from dual;
create sequence user2.increment_sequence nocache;
select user2.increment_sequence.nextval from dual;
create table user1.drop_table(id number);
create table user2.create_table(id number);
create view user1.change_view as select 1 a from dual;
create view user2.change_view as select 2 a from dual;
create table user1.allocate_extent(id number);
create table user2.allocate_extent(id number);
insert into user2.allocate_extent values(1);
rollback;
You are correct that DBMS_METADATA_DIFF does not work for CREATE or DROP. Trying to diff an object that only exists in one schema will generate an error message
like this:
ORA-31603: object "EXTRA_TABLE" of type TABLE not found in schema "USER1"
ORA-06512: at "SYS.DBMS_METADATA", line 7944
ORA-06512: at "SYS.DBMS_METADATA_DIFF", line 712
However, dropping and adding objects may be easy to script with the following:
--Dropped objects
select 'DROP '||object_type||' USER1.'||object_name v_sql
from
(
select object_name, object_type from dba_objects where owner = 'USER1'
minus
select object_name, object_type from dba_objects where owner = 'USER2'
);
V_SQL
-----
DROP TABLE USER1.DROPPED_TABLE
--Added objects
select dbms_metadata.get_ddl(object_type, object_name, 'USER2') v_sql
from
(
select object_name, object_type from dba_objects where owner = 'USER2'
minus
select object_name, object_type from dba_objects where owner = 'USER1'
);
V_SQL
-----
CREATE TABLE "USER2"."CREATED_TABLE"
( "ID" NUMBER
) SEGMENT CREATION DEFERRED
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
TABLESPACE "USERS"
The alters can be handled with a SQL statement like this:
select object_name, object_type, dbms_metadata_diff.compare_alter(
object_type => object_type,
name1 => object_name,
name2 => object_name,
schema1 => 'USER2',
schema2 => 'USER1',
network_link1 => 'MYSELF',
network_link2 => 'MYSELF') difference
from
(
select object_name, object_type from dba_objects where owner = 'USER1'
intersect
select object_name, object_type from dba_objects where owner = 'USER2'
) objects;
OBJECT_NAME OBJECT_TYPE DIFFERENCE
----------- ----------- ----------
ADD_COLUMN TABLE ALTER TABLE "USER2"."ADD_COLUMN" DROP ("SOME_COLUMN")
ALLOCATE_EXTENT TABLE -- ORA-39278: Cannot alter table with segments to segment creation deferred.
CHANGE_VIEW VIEW -- ORA-39308: Cannot alter attribute of view: SUBQUERY
INCREMENT_SEQUENCE SEQUENCE ALTER SEQUENCE "USER2"."INCREMENT_SEQUENCE" RESTART START WITH 3
Some notes about these results:
ADD_COLUMN works as expected.
ALLOCATE_EXTENT is probably a false positive, I doubt you care about deferred segment creation. It is very unlikely to affect your system.
CHANGE_VIEW does not work at all. But as with the previous metadata queries, there should be a relatively easy way to build this script using DBA_VIEWS.
INCREMENT_SEQUENCE works too well. Most of the time an application does not care about the sequence values. But sometimes when things get out of sync you need to change them. This RESTART START WITH syntax can be very helpful. You don't need to drop or re-create the indexes, or mess with the increment by multiple times. This syntax is not in the 12c manual. In fact, I cannot find it anywhere on Google. Looks like this package is using undocumented features.
Some other notes:
The package can be very slow sometimes.
If network links on the server are a problem you will need to run it through a local instance with links to both servers.
There may be false positives. Sometimes it returns a row with just a space in it.
It is possible to fully automate this process. But based on the issues above, and my experience with all such automated tools, you should not trust it 100%.
i am creating a table in sqlserver database using query like "SELECT Table1.* INTO Table2 FROM Table1"
here Table2 created successfully but it is not showing my database
when i again fire this query than it gives error that Table2 is already created but i can't see this in my database
i am refreshing my database also
so please help me if anyone has solution..
#Ramesh has the right idea. In some situations (I think if your user is in the db_owner role?), SELECT INTO tables are created in the schema (the SQL 2005+ terminology) associated with your login. This may be something like YOURDOMAIN\username.Table2. If you go to select again from the same login, it will work fine, but chances are that other users will not be searching in your schema.
When in doubt, explicitly create the table in the dbo schema:
SELECT Table1.*
INTO dbo.Table2
FROM Table1
have you tried with the username.Table1, like
dbo.table
Its very important to append the username for any db object, it enforces the user to select the objects which he got permission to view