SQL Server 2000 And the IS_MEMBER() function.

I have implemented row-based security via checking whether a user is a member of a role that has permission to see certain rows that are flagged as secure. The implementation consists of checking if the user is a member of the appropriate role inside the stored procedure that serves the data via the IS_MEMBER() function.  If the user is a member of the secure role, then the procedure returns the appropriate rows.  If not, they can still use the same procedure to see rows that were not secured.

 

After installing a beta of the software at the client's site, I found a strange problem.  None of the users that were assigned the appropriate permissions could get into the secure data.  I checked and double-checked and triple-checked the permissions to make sure that they were a member of the appropriate role. No problems there. Then I noticed that my DBA had set everyone up as a db_owner to start with. I started doing some testing with the IS_MEMBER() function and found this:

 

If user is made a member of dbo or db_owner, then IS_MEMBER always returns 0 for any role other than dbo, regardless of whether the user is a member of that role or not.

 

Example Script:

SETUSER ‘MyDboUser’

PRINT 'The Current User is: ' + CAST(CURRENT_USER as varchar)

DECLARE @i int

SET @i = IS_MEMBER(‘MyCustomRole’)

 

IF @i = 0 BEGIN

    PRINT 'The User is not a member of the role.'

END

ELSE IF @i = 1 BEGIN

    PRINT 'The User is a member of the role.'

END

ELSE IF @i IS NULL BEGIN

    PRINT 'The role was not found'

END

 

exec sp_addrolemember @rolename= ‘MyCustomRole’, @membername= ‘MyDboUser’

SET @i = IS_MEMBER('JUA')

 

IF @i = 0 BEGIN

    PRINT 'The User is not a member of the role.'

END

ELSE IF @i = 1 BEGIN

    PRINT 'The User is a member of the role.'

END

ELSE IF @i IS NULL BEGIN

    PRINT 'The role was not found'

END

 

Output:

The Current User is: dbo

The User is not a member of the role.

‘MyDboUser’ added to role 'MyCustomRole'.

The User is not a member of the role.

 

This is very strange to me.  I would think if IS_MEMBER was going to return a default value for dbo’s, it would be that they were a member of every role—the way this is set up now defies my understanding.  Perhaps you know why IS_MEMBER was designed this way—if so, please enlighten the rest of us! J

 

The solution to this problem is simple--either grant db_owner access to the secure rows (bad solution) or finish the script to assign role-based security correctly, abandoning the db_owner for basic users (good solution).   I still say the behaviour of the IS_MEMBER() function is strange in this case though.  I thought about this some more, and came up with a--

 

Question: What happens when the user is a member of a role that is a parent of a child role?

 

Suppose I have a role called Parent, and a role called Child.  I create a user ‘MyUser’, and add him to the Child Role.  Then I add the Parent role to the Child role, granting the parent role all permissions of the child, plus additional permissions.  Now, I call SETUSER ‘MyUser’. Finally, I call IS_MEMBER(‘Parent’) and IS_MEMBER(‘Child’). 

 

SELECT IS_MEMBER(‘Parent’) – returns 0

SELECT IS_MEMBER(‘Child’) –returns 1

 

Now, I change the role assignments so that MyUser is a member of Parent, but not Child.

 

SELECT IS_MEMBER(‘Parent’) –returns 1

SELECT IS_MEMBER(‘Child’) –returns 1

 

So it appears that even though you cannot tell if a dbo has been assigned to specific roles, you can tell if one role has been assigned to specific roles. Strange.

Comments

# Take Outs for 26 March 2004.

Saturday, March 27, 2004 2:10 AM by TrackBack

Take Outs for 26 March 2004.

# re: SQL Server 2000 And the IS_MEMBER() function.

Sunday, June 06, 2004 1:27 AM by Nathan Smith

I just ran into the IS_MEMBER/db_owner problem myself. Thanks for letting me know I'm not crazy. :P

Unfortunately, I need to allow users with db_owner affiliation use the app I'm developing, so I can't use your "good fix" in its pure form. Probably going to go w/ something like:

if IS_MEMBER('FP_Admin') or IS_MEMBER('db_owner')
(
-- some stuff
)

Does that look like the best workaround to you?

nate

# re: SQL Server 2000 And the IS_MEMBER() function.

Sunday, June 06, 2004 1:48 AM by Chris McKenzie

That's the only work around that I can think of in your case. I'm glad you found my post helpful :)

Chris

# re: SQL Server 2000 And the IS_MEMBER() function.

Friday, June 18, 2004 5:23 AM by Justin

I imagine that this is the default behaviour so that people who are server admin (and therefore members of dbo) don't have access to the data (well, at least not from the front-end).

Thanks for your post though, as I've been puzzling over this one too.

# Things I've learned in the year 2004

Saturday, January 01, 2005 9:02 PM by TrackBack

# A Question about SQL Server Security When Building Client-Server Applications.

Friday, March 18, 2005 3:53 PM by TrackBack

# A Question about SQL Server Security When Building Client-Server Applications.

Friday, March 18, 2005 9:08 PM by TrackBack

# re: SQL Server 2000 And the IS_MEMBER() function.

Monday, October 08, 2007 10:49 PM by Drask

This is from a long time ago, but nobody ever really addressed the big problem here, that many of us use SQL Server roles for application level security (we can debate the merits of that elsewhere). If you are giving a demo and need to give a user db_owner privileges to ensure they have access to all the tables and can reset anything they need to, suddenly the rest of your security goes haywire. Obviously you would want to carefully set the permissions to the different tables in a production environment, but a routine like below can tell you if a person is part of a role even if they are db_owner.

declare @username sysname

declare @role sysname

declare @return bit

set @role='dude'

set @username='ajc'

if exists( select 1 from sysusers where name=@role and issqlrole=1 )

BEGIN

if exists(

select

1

from( select uid from sysusers where name=@role and issqlrole=1 )role

inner join sysmembers on groupuid=role.uid

inner join ( select uid from sysusers where name=@username and issqluser=1 and hasdbaccess=1 )users on sysmembers.memberuid=users.uid

)

set @return=1

else

set @return=0

END

select @return result

# re: SQL Server 2000 And the IS_MEMBER() function.

Friday, January 23, 2009 1:09 PM by Alek Davis

Oh, thanks. We have been troubleshooting this issue for a couple of hours, and this post helped us figure out the reason of the problem. At the very least, Microsoft should've included a remark about this behavior in the documentation of IS_MEMBER.

Leave a Comment

(required) 
(required) 
(optional)
(required)