I am pretty sure that if I was a fish, I would not survive long enough to grow old as I would fall for the first piece of bait hanging from a lovely, shiny thing that I could see.
The only defence that I have is that, as I’m still a Junior DBA, I can make these mistakes as long as
a). they are not extremely serious (no dropping production databases for this guy!), and
b). I’m expected to learn from them and not repeat them!
And like most things, it started innocently enough. A simple support ticket coming in with the following error message.
Msg 229, Level 14, State 5, Line 65
The SELECT permission was denied on the object ‘Removable’, database ‘LocalTesting’, schema ‘Superflous’.
What I did:
I saw this error message and immediately thought to myself
AH! No problems, they just need SELECT permissions on that object. 2 second job.
And seeing as the ticket was nice enough to provide the login and user that was receiving the error message (we’ll say it was a user called “NewUser”), I could join that with the error message and grant permissions.
GRANT SELECT ON OBJECT::Superflous.Removable TO NewUser;
Following this was a quick test to impersonate myself as the user and see if it works;
— Test 01.
EXECUTE AS USER = ‘NewUser’;
SELECT USER_NAME(), SUSER_SNAME();
SELECT * FROM dbo.GenericView;
As far as I was aware, I was happy it worked; the user, once notified, was happy it worked and I went on my merry way to grab some celebratory coffee.
Until on the way back I bumped into my Senior DBA and told him proudly what I had done…
What I should have done:
The following is a simplified reproduction of that conversation…
>Is that a new View?
>> Is that a new User?
> No…although it’s called New.
>> Could they SELECT from that View before?
> Yeah, as far as I know.
>> Alright, so did anything change before the call?
> eh…I didn’t check
>> Okay, from now on: Check.
It was at that stage that we started getting other tickets in from other users with the same error message. So rather than fixing the underlying problem, I had fixed a symptom for a single user.
The symptom was the User not having permission to select, but the underlying problem was that the View had changed.
At this stage I was still confused as it’s a view, what does it matter if the query creating it has changed, how could this have broken permissions?
Again, jumping the gun, I didn’t check…
Our problem view has two different schemas and when we check the ownership of the two different schemas, we get the following:
— Who owns what?
SELECT dp.name AS Owner, s.*
FROM sys.schemas AS s
JOIN sys.database_principals AS dp ON s.principal_id = dp.principal_id
WHERE s.name in (‘dbo’, ‘Superflous’);
How is this the answer?
Technically, the answer is Ownership Chains.
Superflous.Removable table was in a different database on it’s
dbo schema where the owner of the view (
dbo) had permissions to select from.
Since the owner of the view (OV) had permissions on this schema and the OV gave select permissions on the view to the user (NU), the NU inherited the OV’s permissions.
- So SQL Server hit the view, saw it was owned by the OV and didn’t need to check permissions for our NU.
- The view first hit the table
dbo.Foo, saw that it was owned by OV and so didn’t need to check permissions.
- Now the view calls across to the other database, see’s the owner is not the OV so checks the permissions.
- However the OV has access permissions on this table so the NU gets these access permissions, therefore we have no problem!
Now we had recently done a change to have the information from the other database brought over to our database via Replication.
This meant a re-write of our View using the new table and schema with it’s new owner. This new schema that our NU or the OV did not have permissions for.
What this meant was the same procedure was followed by the SQL Server engine with the only difference being that, instead of going across to the other database, it went to our new schema
Superflous.Removable . It saw the OV did not have access permissions, so it denied access permissions for our NU.
So basically, when
NewUser went to select from our view, they hit the new schema, SQL Server realised it needed to check their permissions and, when none were found, access was denied.
All I had done by jumping the gun and fixing the symptom was made it so that when SQL Server traversed down the ownership chain for the view and came to the new schema, it checked permissions, found the SELECT permission for only this user and continued on.
This was the reason that the view worked for the user but no one else!
MyStuff database principal should not be the owner of our Removable table, in fact the
Superflous schema should not even exist, so it was a simple matter of transferring ownership to
ALTER AUTHORIZATION ON SCHEMA::Superflous TO dbo;
Now all the users, who have read access on the
dbo schema, are able to use this view with no further hassles.
Problem solved! Right?
Stop Jumping the Gun!
All the above is what I did.
Trying to fix the permission error, I granted SELECT permission.
Trying to fix the ownership chain, I transferred ownership.
Mainly in trying to fix the problem, I continually jumped the gun.
Which is why I am still a Junior DBA.
What my Senior DBA did was fix the replication script so the new schema wouldn’t get created in the first place, and the table would get created in
Which is why he’s my Senior DBA.
Jumping the gun isn’t going to give you a head start. It is just going to delay you. Knowing the problems, as well as knowing the solutions, is the answer.
I’m learning the problems…I’ll have the solutions soon, and I aim to share them too.