IS_ROLEMEMBER not working properly - sql

I need to check whether a user has the role DatabaseMailUserRole.
I tried this:
SELECT IS_MEMBER('DatabaseMailUserRole')
SELECT IS_ROLEMEMBER ('DatabaseMailUserRole', '<loginname>')
Both return a value that is either correct or incorrect.
This means for several logins it returns the right value, but for others not.

DatabaseMailUserRole is sql server, from what i am aware of. An issue with your logic is that DatabaseMailUserRole is a database role, and therefore maps to users, not logins.
users (database level) and logins (server level) are distinct objects To access a database on a server, a user needs both a login name and a user name. Login and user objects for the same person may, but are not required to, share the same name
For example
CREATE USER someuser FROM LOGIN 'domain\login';
GO
EXEC sp_addrolemember 'DatabaseMailUserRole','someuser';
GO
/* the following will fail because only users can be added to database role DatabaseMailUserRole */
EXEC sp_addrolemember 'DatabaseMailUserRole','domain\login';
GO
EXECUTE AS login = 'domain\login'
SELECT IS_MEMBER('DatabaseMailUserRole') -> true
/* login 'domain\login' has username someuser */
SELECT CURRENT_USER -> 'someuser'
SELECT SYSTEM_USER -> 'domain\login'
SELECT IS_ROLEMEMBER ('DatabaseMailUserRole', 'domain\login') -> NULL
SELECT IS_ROLEMEMBER ('DatabaseMailUserRole', 'someuser') -> True
Therefore IS_ROLEMEMBER should get called w/ arguments as below:
SELECT IS_ROLEMEMBER ('DatabaseMailUserRole', '<user_name>')
/* where user name is one of the following */
SELECT name FROM sys.database_principals WHERE type=N'U';
/* OR */
SELECT USER_NAME()
/* OR */
SELECT CURRENT_USER
you might be getting correct results using logins names in some cases because server level logins can be mapped to a database level user with the same name.

Related

How to allow a view to read from multiple schemas

I have two tables in different schemas, one owned by dbo, the other not. I have a third schema where I'm trying to create a view that reads from those two tables. My understanding is that if dbo is the owner of the view, because it has grant options to all objects in the database, users with only access to the view should be able to query it. But that isn't working, even when I explicitly grant dbo SELECT WITH GRANT to the underlying table in the schema it doesn't own.
I've read a few other posts, but don't see an answer that works: https://stackoverflow.com/a/4134892/1499015, Ownership Chaining and Tutorial: Ownership Chains and Context Switching
I assume I'm doing something wrong with permission chaining, but I can't figure out what the problem is.
--use master
--GO
--create login testuser with password='******************'
-- principal3 will own schema3
--create login principal3 with password='*****************'
--use testingDB
--GO
create user testuser for login testuser
create user principal3 for login principal3
GO
-- testrole that will only have select permissions on schema2
CREATE ROLE testrole
exec sp_addrolemember 'testrole', 'testuser'
GO
-- the first schema is owned by dbo
CREATE SCHEMA schema1 authorization dbo
GO
-- the schema where the view will live, also owned by dbo
create schema schema2 authorization dbo
GO
-- schema3 owned by principal3
create schema schema3 authorization principal3
go
-- tables in schema1 and schema3
CREATE TABLE schema1.testtable1 (
id int
, testvalue sysname
)
GO
CREATE TABLE schema3.testtable3 (
id int
, testvalue sysname
)
GO
-- some base data
INSERT schema1.testtable1
SELECT 1, 'a'
UNION SELECT 2, 'b'
UNION SELECT 3, 'c'
INSERT schema3.testtable3
SELECT 1, 'hot dogs rule'
UNION SELECT 2, 'pizza sucks'
UNION SELECT 3, 'haw just kidding that''s obviously backwards'
GO
-- view in schema2 that queries tables in schema1 and schema3, owned by dbo
CREATE VIEW schema2.testview
AS
SELECT t1.testvalue as v1
, t3.testvalue as v3
FROM schema1.testtable1 t1
INNER JOIN schema3.testtable3 t3 ON t1.id = t3.id
GO
GRANT SELECT ON schema::schema1 TO dbo WITH GRANT OPTION
GRANT SELECT ON schema::schema3 TO dbo WITH GRANT OPTION
ALTER AUTHORIZATION ON schema2.testview TO dbo
-- give testrole permission to query the view
GRANT SELECT ON schema2.testview to testrole
GO
EXECUTE AS USER = 'testuser';
SELECT USER_NAME();
BEGIN TRY
select top 100 * from schema2.testview
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH
REVERT;
SELECT USER_NAME();
When I run this code, I get
The SELECT permission was denied on the object 'testtable3', database 'testingDb', schema 'schema3'.
I've also tried to create a schema owner user for the schema that the view lives in, and granting that user select with grant to the underlying tables, but it's still not working.
Any ideas what I'm doing wrong?
Thanks!
The documentation says:
When an object is accessed through a chain, SQL Server first compares
the object's owner to the owner of the calling object (the previous
link in the chain). If both objects have the same owner, permissions
on the referenced object are not checked. Whenever an object accesses
another object that has a different owner, the ownership chain is
broken and SQL Server must check the caller's security context.
In this case, when the object testtable3 is accessed, SQL Server first compares testtable3 owner to the owner of the testview. If both objects have the same owner, permissions on the referenced object are not checked. It does not matter if you GRANT dbo rights on testtable3, even WITH GRANT (he this right anyway). It matters that the owner of testtable3 is the same as the owner of the testview.
For example, try:
ALTER AUTHORIZATION ON SCHEMA::schema3 TO dbo
And it will work.
If the owners are different, the ownership chain is broken and SQL Server must check the caller's security context. In this case: does testuser have rights to select from testtable3?.
Therefore, if the owners are different you need to do this to make it work:
GRANT SELECT ON schema3.testtable3 TO testuser

Find all databases where particular user exists and its role

I have a huge instance containing 1000+ databases. I need to find a way to query entire instance and find databases that contain particular user and what role this user has. I am not interested whether the user is orphanded. I just want to know which databases have this user and which do not.
Lets say that my user is called TestUser. Databases that do not contain this user should return NULL.
I would like the results in the following format:
Column1 - Database Name
Column2 - UserName (if exists or else NULL)
Column3 - UserRole (if exists or else NULL)
Under the assumption that you are not looking for issuing 1000+ selects, one (extremely ugly) solution would be:
SELECT 'DB_1' , UserName , UserRole
FROM DB_1.UsersTable
WHERE Username = 'TestUser'
UNION
SELECT 'DB_2' , UserName , UserRole
FROM DB_2.UsersTable
WHERE Username = 'TestUser'
:
:
Another solution is to use DYNAMIC SQL:
Collect the list of all the DBs that you to check,
Build a string hosting a select statement like the one above,
Execute the statement.
Again, both methods are shameful.
create table #temp
(
dbname sysname,
dbrole sysname,
dbuser sysname
)
Exec sp_msforeachdb '
if db_id()>4
Begin
insert into #temp
select db_name(), rp.name as database_role, mp.name as database_user
from sys.database_role_members drm
join sys.database_principals rp on (drm.role_principal_id = rp.principal_id)
join sys.database_principals mp on (drm.member_principal_id = mp.principal_id)
End
'
The Roles Part is referenced from here:
Get list of all database users with specified role

can I give SQL Server database name with hyphen like abc-123?

I created a sql server database with name of abc-123, in that I created a table Emp, when I run likeselect * from abc-123.emp; I am getting the results.
But when I am trying to grant some privilege to the user I unable to do that, getting syntax error near hyphen .
will any one help me?
Make sure you are escaping the names with [] (T-SQL) or "" (ANSI SQL). You are using non-standard naming.
-- Sample select
SELECT * FROM [abc-123].[dbo].[emp];
SELECT * FROM "abc-123"."dbo"."emp";
1 - Can you send me an example of the grant TSQL? If you are doing the action from SSMS, right click and script the code.
2 - Here is the link to the GRANT TSQL command. I do not see any syntax like you are trying.
http://technet.microsoft.com/en-us/library/ms188371.aspx
TO 'drupal'#'localhost' IDENTIFIED BY 'Drup#l';
First, it should be [drupal#localhost]. Second, I never seen the IDENTIFIED BY clause. Where are you getting that information from?
3 - Here is a quick TSQL script that creates a badly named database and user. If possible, change the name of the database and user.
Also, if you are granting permissions at the table level other than db_owner (very granular and a-lot of maintenance), then create an user defined database role. Add securables to the role and add your user to the role.
http://technet.microsoft.com/en-us/library/ms187936.aspx
Sample code.
-- Create new database
create database [abc-123]
go
-- Use new database
use [abc-123];
go
-- Create table from sample data
select
[BusinessEntityID]
,[PersonType]
,[NameStyle]
,[Title]
,[FirstName]
,[MiddleName]
,[LastName]
,[Suffix]
,[EmailPromotion]
, cast([AdditionalContactInfo] as varchar(max))
as [AdditionalContactInfoTxt]
, cast([Demographics] as varchar(max))
as [DemographicsTxt]
,[rowguid]
,[ModifiedDate]
into
[abc-123].[dbo].[emp]
from
AdventureWorks2012.Person.Person;
-- Create a login
CREATE LOGIN [drupal#localhost] WITH PASSWORD=N'Ja08n13$', DEFAULT_DATABASE=[abc-123]
GO
-- Create a user
CREATE USER [drupal#localhost] FOR LOGIN [drupal#localhost] WITH DEFAULT_SCHEMA=[dbo]
GO
-- Add to database owner role
EXEC sp_addrolemember 'db_owner', [drupal#localhost]
GO
Output with user in db_owner group.
Use Back Quote for the DB name
select * from `abc-123`.emp;
or, select the existing database with a USE statement and run the query.
USE `abc-123`;
select * from emp;
Use [] round the database name:
SELECT * FROM [abc-123].[dbo].emp;
OR
SELECT * FROM [abc-123].dbo.emp;
if you are using databasename with table name then suppose to specify the schema name also.select * from [abc-123].dbo.emp

T-SQL: SUSER_SNAME vs SUSER_NAME?

The MSDN documentation says for SUSER_SNAME function:
Returns the login identification name from a user's security identification number (SID).
More over, it says for the SUSER_NAME function:
Returns the login identification name of the user.
Nonetheless, when I execute the following SQL statements I get the same result:
SELECT SUSER_NAME();
SELECT SUSER_SNAME();
So, what are differences, and which one shall I use? Is there a situation I should use one rather that the other?
Please advice,
Thanks in advance :)
If you call the function without an argument they will both return the same value. But they do take different arguments:
SUSER_SNAME() takes the varbinary(85) SID of a login as argument
SUSER_NAME() takes the integer principal_id of a login
You can verify this like:
select suser_name(principal_id)
, suser_name(sid)
, suser_sname(principal_id)
, suser_sname(sid)
from sys.server_principals
where name = suser_name()
Only the first and last column will return non-null values.
SUSER_NAME() will return the name associated with an sid that exists in sys.server_principals. The sid must exist in sys.server_principals.
SUSER_SNAME() can do that but also can return the sid of a login if the login is a member of an active directory group
So if you have [CONTOSO\MyGroup] in Active Directory and that group has one user [CONTOSO\MyUser]
And you add that group to SQL Server:
CREATE LOGIN [CONTOSO\MyGroup] FROM WINDOWS;
SELECT SUSER_ID('CONTOSO\MyUser'), SUSER_SID('CONTOSO\MyUser')
will give you
NULL, CONTOSO\MyUser
because CONTOSO\MyUser is not in sys.server_principals but is in A/D

Changes to sysusers and sysxlogins in SQL 2008

I am currently updating a MS SQL 2000 server to SQL 2008. One of the issues highlighted by the Upgrade advisor is that the undocumented table sysxlogins has been removed.
I currently have a procedure that is run by a user 'foo' to determine if the user 'bar' exists in the database blah. If the user exists the user's password is compared to the password that was passed in to the procedure in order to determine if bar is allowed to log in to an application, it looks like this:
#UserName Varchar(50),
#Password Varchar(50)
As
Set NoCount On
------------------------------------------------------------------------------------
-- Check username
------------------------------------------------------------------------------------
If Exists
(
select top 1 name
from blah.dbo.sysusers With (NoLock)
where name = #UserName
)
Begin
------------------------------------------------------------------------------------
-- Check Password
------------------------------------------------------------------------------------
If Not Exists
(
Select *
From master.dbo.sysxlogins With (NoLock)
Where srvid IS NULL
And name = #Username
And ( ((#Password is null) or (#Password = '') and password is null)
Or (pwdcompare(#Password, password, (CASE WHEN xstatus&2048 = 2048 THEN 1 ELSE 0 END)) = 1))
)
Begin
Return 2
End
Else
Begin
------------------------------------------------------------------------------------
-- Check Role
------------------------------------------------------------------------------------
Select usg.name
From blah.dbo.sysusers usu
left outer join (blah.dbo.sysmembers mem inner join blah.dbo.sysusers usg on mem.groupuid = usg.uid) on usu.uid = mem.memberuid
left outer join syslogins lo on usu.sid = lo.sid
where usu.name = #Username
and usg.name not like 'db_%'
Return 0 -- Username and password correct
End
End
Else
Begin
Return 1 -- Username incorrect
End
This all works fine under SQL 2000, yet I must now pay the price of using undocumented system tables and make it work under 2008.
There are two problems with this, the first problem is that foo can no longer see all of the database users when executing:
select * from blah.dbo.sysusers
or Microsoft's recommended alternative:
select * from blah.sys.database_principals
I understand that this is due to the fact that members of the public role no longer have access to object meta data unless they are a member of sysadmin or have the View Definition permission on the object.
It is not possible for foo to be a member of sysadmin, so as far as I understand I need to grant foo the View Definition permission, but on which object? I don't think I do it on the system view, so do I do it on every single user?
Secondly, and similarly, I need to change my reference to sysxlogins to sys.sql_logins. Again foo can only see itself and sa when executing
select * from sys.sql_logins
How can I get foo to see all of the server logins in this list?
There will no doubt be similar problems when accessing sysmembers and syslogins later on in the code but hopefully an understanding of the two examples above will help me to sort the rest out.
Thanks in advance,
You can grant the SELECT right directly on sys.database_principals, as long as the login has a user in the master database. For example:
use master
create user MyUser for login MyUser
grant select on sys.database_principals to MyUser
Then, in SQL Server 2008, passwords are encrypted, even for the administrator. You can, however, verify a password by trying to change it. The change procedure will give an error if the old password is incorrect.
declare #rc int
begin try
exec #rc = sp_password 'welcome', 'welcome', 'MyUser'
end try
begin catch
set #rc = ERROR_NUMBER()
end catch
-- Should be 0 on success
select #rc
For this to work, you have to disable Enforce password policy in the Login Properties dialog. Otherwise, the policy would prevent you from changing your password too often.
I think GRANT SELECT ON... is more troublesome as one have to add the user to the master database. The below was the solution for me:
USE master
GRANT VIEW ANY DEFINITION TO foo
If you have an app that works on various versions of SQL you need to check if the server version is higher then 8 (GRANT VIEW ANY DEFINITION works from SQL 2005 though it seemes not be needed there).