T-SQL: SUSER_SNAME vs SUSER_NAME? - sql

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

Related

Identifying a User and a Machine [duplicate]

My work company has a MSSQL server 2005. I have two questions about finding out current log user and any way to send out a warning message:
First question is if there is any T-SQL or SP available to find out current login user name and machine name. If the user is using SQL server sa name to remotely access to SQL server, is there any way to find out that user's windows name (the name to log to the windows)?
My next question is that if I can get the user name or id, is there any way to send out a warning message such as "currently SQL server is clean up or backup, please do not log in at this time". I guess it may be difficult. I may have to send an email out to the user.
The SQL server is only accessible in the company. The SQL server has a list of users as login users: windows users, SQL users and sa.
SELECT SUSER_SNAME(), HOST_NAME()
If the connection is "sa" (or any other SQL login) then you can't find the domain/windows user name. SQL Server only knows it's "sa" or that SQL login.
HOST_NAME may not be reliable either, it can be set in the connection string ("Application Name"). Or it could be vague eg "Microsoft Office" for by default for Access, Excel etc
You could backtrack via client_net_address in sys.dm_exec_connections and match MAC address to IP and find out who is logged on...
An easy way to find out both host and user is
EXEC sp_who2;
where you get some other information that can be good to know, as if the user is active and so on...
It does not resolve the issues that gbn announced.
Thanks for all your suggestions first. I tried all the methods and I think Joakim Backman's method meet my need. Here is summary of what I find out.
Query of sys.syslogins only list login information. The accdate does not give the current login user timestamp. I tried to login from another application to my SQL and this query does not list the login.
SELECT SUSER_SNAME(), HOST_NAME() only list one user on SQL server. For example, I log in in as my name to SQL server. The result of this query only lists my name and machine name. This query does not list current users on the SQL server.
exec sp_who2 lists information I need. It lists current user name machine name, active status, db name user's access, and command used.
In order to get the information I use in SP, I have to filter and join the information with other tables, such as emails. Here is the codes I use:
DECLARE #retTable TABLE (
SPID int not null
, Status varchar (255) not null
, Login varchar (255) not null
, HostName varchar (255) not null
, BlkBy varchar(10) not null
, DBName varchar (255) null
, Command varchar (255) not null
, CPUTime int not null
, DiskIO int not null
, LastBatch varchar (255) not null
, ProgramName varchar (255) null
, SPID2 int not null
, REQUESTID INT
)
INSERT INTO #retTable EXEC sp_who2
SELECT Status, Login, HostName, DBName, Command, CPUTime, ProgramName -- *
FROM #retTable
--WHERE Login not like 'sa%' -- if not interested in sa
ORDER BY Login, HostName

IS_ROLEMEMBER not working properly

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.

How can i create view using the SYSTEM_USER's permission path in query

I am attempting to make a view of a table that uses the current user's user name (System_user) with their log in information ([permission path]) which tells me which subset of the data they are allowed to see in the view. Views cannot be created with variables or temporary tables so made an attempt at user defined functions to return a table but it appears to be a bit beyond my current skills.
It works as a Query
DECLARE #user as varchar(50)
SELECT #user = SYSTEM_USER
CREATE TABLE LoginInformation(
[account name] varchar(50),
[type] varchar(50),
[privilege] varchar(50),
[mapped login name] varchar(50),
[permission path] varchar(50)
)
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = #user, #Option = 'all';
SELECT * FROM SomeTableName
WHERE Permission IN (SELECT [permission path] FROM LoginInformation)
When creating a view I get stuck because I cannot declare a variable:
DECLARE #user as varchar(50)
SELECT #user = SYSTEM_USER
When I tried user defined functions i got held up because I could not Insert an Executed statement
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = #user, #Option = 'all';
I also do not appear to be able to do the simplest solution:
INSERT LoginInformation
EXEC xp_LoginInfo #AcctName = SYSTEM_USER, #Option = 'all';
Ideally i would have a function that took System_user as a parameter and gave me a table of just that user's [permission path]. I could then use that in my view's creation.
WHERE Permission IN (MyFunctionName(SYSTEM_USER))
xp_LoginInfo is a stored procedure. You can see its underlying code by running the following T-SQL statement:
sp_helptext xp_LoginInfo
Which produces the output:
create procedure sys.xp_logininfo
#acctname sysname = null, -- IN: NT login name
#option varchar(10) = null, -- IN: 'all' | 'members' | null
#privilege varchar(10) = 'Not wanted' OUTPUT -- OUT: 'admin' | 'user' | null
as
set nocount on
etc ...
My suggestion is to take the underlying SQL, tweak it if needed and use it in your view.
While doing additional research I found a much easier and different way of accomplishing my goal. I went with the Is_Member function. It accesses the permission path without having to work with SYSTEM_USER. I have done a few tests and it appears to do what I want.
CREATE VIEW [dbo].[vw_SomeTableName]
SELECT * FROM SomeTableName
where WHERE Is_Member( UserName ) = 1 )
The Is_Member function indicates whether the current user is a member of the specified Microsoft Windows group or SQL Server database role.
IS_MEMBER ( { 'group' | 'role' } )
Return value:
Current user is not a member of group or role, returns 0.
Current user is a member of group or role, returns 1.
Either group or role is not valid. When queried by a SQL Server login or a login using an application role, returns NULL for a Windows group.
For more on Is_Member check out msdn.microsoft.com on Is_Member or look at some other stack overflow questions.

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).

How to find out user name and machine name to access to SQL server

My work company has a MSSQL server 2005. I have two questions about finding out current log user and any way to send out a warning message:
First question is if there is any T-SQL or SP available to find out current login user name and machine name. If the user is using SQL server sa name to remotely access to SQL server, is there any way to find out that user's windows name (the name to log to the windows)?
My next question is that if I can get the user name or id, is there any way to send out a warning message such as "currently SQL server is clean up or backup, please do not log in at this time". I guess it may be difficult. I may have to send an email out to the user.
The SQL server is only accessible in the company. The SQL server has a list of users as login users: windows users, SQL users and sa.
SELECT SUSER_SNAME(), HOST_NAME()
If the connection is "sa" (or any other SQL login) then you can't find the domain/windows user name. SQL Server only knows it's "sa" or that SQL login.
HOST_NAME may not be reliable either, it can be set in the connection string ("Application Name"). Or it could be vague eg "Microsoft Office" for by default for Access, Excel etc
You could backtrack via client_net_address in sys.dm_exec_connections and match MAC address to IP and find out who is logged on...
An easy way to find out both host and user is
EXEC sp_who2;
where you get some other information that can be good to know, as if the user is active and so on...
It does not resolve the issues that gbn announced.
Thanks for all your suggestions first. I tried all the methods and I think Joakim Backman's method meet my need. Here is summary of what I find out.
Query of sys.syslogins only list login information. The accdate does not give the current login user timestamp. I tried to login from another application to my SQL and this query does not list the login.
SELECT SUSER_SNAME(), HOST_NAME() only list one user on SQL server. For example, I log in in as my name to SQL server. The result of this query only lists my name and machine name. This query does not list current users on the SQL server.
exec sp_who2 lists information I need. It lists current user name machine name, active status, db name user's access, and command used.
In order to get the information I use in SP, I have to filter and join the information with other tables, such as emails. Here is the codes I use:
DECLARE #retTable TABLE (
SPID int not null
, Status varchar (255) not null
, Login varchar (255) not null
, HostName varchar (255) not null
, BlkBy varchar(10) not null
, DBName varchar (255) null
, Command varchar (255) not null
, CPUTime int not null
, DiskIO int not null
, LastBatch varchar (255) not null
, ProgramName varchar (255) null
, SPID2 int not null
, REQUESTID INT
)
INSERT INTO #retTable EXEC sp_who2
SELECT Status, Login, HostName, DBName, Command, CPUTime, ProgramName -- *
FROM #retTable
--WHERE Login not like 'sa%' -- if not interested in sa
ORDER BY Login, HostName