Uma consulta que lista todos os usuários mapeados para um determinado login

19

Ao examinar as propriedades de um logon específico, é possível ver uma lista de usuários mapeados para esse logon: insira a descrição da imagem aqui

Criei um perfil do SSMS (SQL Server Management Studio) e vejo que o SSMS se conecta a cada banco de dados, um de cada vez, e recupera informações de sys.database_permissions

É possível escrever uma única consulta que recupere as informações de mapeamento do usuário mostradas acima ou sou forçado a usar um cursor ou sp_MSforeachdb ou algo parecido?

Michael J Swart
fonte
5
Por favor , não use sp_MSforeachdb .
Aaron Bertrand

Respostas:

15

Aqui está uma maneira de usar o SQL dinâmico. Não há realmente nenhuma maneira de fazer isso sem iterar, mas essa abordagem é muito mais segura do que as opções não documentadas, sem suporte e com errossp_MSforeachdb .

Isso obterá uma lista de todos os bancos de dados online, o usuário mapeado (se existir), juntamente com o nome do esquema padrão e uma lista separada por vírgula das funções às quais eles pertencem.

DECLARE @name SYSNAME = N'your login name'; -- input param, presumably

DECLARE @sql NVARCHAR(MAX) = N'';

SELECT @sql += N'UNION ALL SELECT N''' + REPLACE(name,'''','''''') + ''',
  p.name, p.default_schema_name, STUFF((SELECT N'','' + r.name 
  FROM ' + QUOTENAME(name) + N'.sys.database_principals AS r
  INNER JOIN ' + QUOTENAME(name) + N'.sys.database_role_members AS rm
   ON r.principal_id = rm.role_principal_id
  WHERE rm.member_principal_id = p.principal_id
  FOR XML PATH, TYPE).value(N''.[1]'',''nvarchar(max)''),1,1,N'''')
 FROM sys.server_principals AS sp
 LEFT OUTER JOIN ' + QUOTENAME(name) + '.sys.database_principals AS p
 ON sp.sid = p.sid
 WHERE sp.name = @name '
FROM sys.databases WHERE [state] = 0;

SET @sql = STUFF(@sql, 1, 9, N'');

PRINT @sql;
EXEC master.sys.sp_executesql @sql, N'@name SYSNAME', @name;
Aaron Bertrand
fonte
1
nota interessante, eu tive que adicionar agrupamentos explícitos às colunas p.name e p.default_schema_name para que a união funcionasse corretamente #
Michael J Swart
@ MichaelJSwart Ah, sim, já deparei com isso antes, quando os bancos de dados têm agrupamentos diferentes (algumas colunas de metadados usam o agrupamento do servidor, mas outras herdam o agrupamento do banco de dados). Espero que as únicas pessoas que realmente sempre queimaduras são aqueles que insistem em usar personagens malucos em nomes de entidades que são apenas suportados em algum agrupamento obscuro ...
Aaron Bertrand
7

Este script é ligeiramente modificado a partir de um script mencionado em fará o que você está procurando. Substitua 'ThursdayClass' pelo login para o qual você precisa de informações. https://www.simple-talk.com/sql/sql-tools/the-sqlcmd-workbench/

    SET NOCOUNT ON
    CREATE TABLE #temp
        (
          SERVER_name SYSNAME NULL ,
          Database_name SYSNAME NULL ,
          UserName SYSNAME ,
          GroupName SYSNAME ,
          LoginName SYSNAME NULL ,
          DefDBName SYSNAME NULL ,
          DefSchemaName SYSNAME NULL ,
          UserID INT ,
          [SID] VARBINARY(85)
        )

    DECLARE @command VARCHAR(MAX)
    --this will contain all the databases (and their sizes!)
    --on a server
    DECLARE @databases TABLE
        (
          Database_name VARCHAR(128) ,
          Database_size INT ,
          remarks VARCHAR(255)
        )
    INSERT  INTO @databases--stock the table with the list of databases
            EXEC sp_databases

    SELECT  @command = COALESCE(@command, '') + '
    USE ' + database_name + '
    insert into #temp (UserName,GroupName, LoginName,
                        DefDBName, DefSchemaName,UserID,[SID])
         Execute sp_helpuser
    UPDATE #TEMP SET database_name=DB_NAME(),
                     server_name=@@ServerName
    where database_name is null
    '
    FROM    @databases
    EXECUTE ( @command )

    SELECT  loginname ,
            UserName ,
            Database_name
    FROM    #temp
    WHERE   LoginName = 'ThursdayClass' 
SqlWorldWide
fonte
Obrigado Taiob, isso funciona bem, (eu colocaria a coluna database_name entre colchetes ('[' e ']')) #
11184 Michael J Swart
5

Tente sp_dbpermissions . Provavelmente lhe dará mais informações do que você precisa, mas fará o que quiser.

Uma vez instalado, execute isso.

sp_dbpermissions @dbname = 'All', @LoginName = 'LoginName'

Aviso justo no momento em que ele faz uma correspondência "like"; portanto, se outros logins forem semelhantes e coincidirem, você também os verá. Por exemplo, MyLogine MyLoginForThisambos corresponderão MyLogin. Se isso for um problema, eu tenho uma versão que ainda não foi lançada e você pode desativá-la. Avise-me e posso enviá-lo por e-mail.

Kenneth Fisher
fonte
4

Aqui está uma solução do PowerShell:

import-module sqlps;

$s = new-object microsoft.sqlserver.management.smo.server '.'
foreach ($db in $s.Databases | where {$_.IsAccessible -eq $true}) {
   $u = $db.users | where {$_.Login -eq 'foobar'}
   if ($u -ne $null) { #login is mapped to a user in the db
       foreach ($role in $db.Roles) {
           if ($role.EnumMembers() -contains $u.Name) {
               $u | select parent, @{name="role";expression={$role.name}}, name
           }
       }
   }
}
Ben Thul
fonte
4

Infelizmente, você terá que percorrer todos os bancos de dados para obter as informações. Você vai querer se juntar sys.database_principalsa sys.server_principalspara cada correspondência de banco de dados no SID.

Não use sp_msforeachdbcomo é conhecido por perder bancos de dados às vezes.

Nic
fonte
1

Eu estava procurando uma resposta semelhante e encontrei o seguinte: https://www.pythian.com/blog/httpconsultingblogs-emc-comjamiethomsonarchive20070209sql-server-2005_3a00_-view-all-permissions-_2800_2_2900_-aspx/ . E sim, ele usa o temido sp_MSforeachDB, mas acho que esse cara faz um rap ruim às vezes ... ;-)

Vou postar o SQL aqui para facilitar a cópia-pasta ( NÃO estou recebendo crédito por isso, apenas tornando-o facilmente acessível!):

DECLARE @DB_Users TABLE (DBName sysname, UserName sysname, LoginType sysname
, AssociatedRole varchar(max), create_date datetime, modify_date datetime)

INSERT @DB_Users
EXEC sp_MSforeachdb
'use [?]
SELECT ''?'' AS DB_Name,
case prin.name when ''dbo'' then prin.name + '' (''
    + (select SUSER_SNAME(owner_sid) from master.sys.databases where name =''?'') + '')''
    else prin.name end AS UserName,
    prin.type_desc AS LoginType,
    isnull(USER_NAME(mem.role_principal_id),'''') AS AssociatedRole, 
    create_date, modify_date
FROM sys.database_principals prin
LEFT OUTER JOIN sys.database_role_members mem
    ON prin.principal_id=mem.member_principal_id
WHERE prin.sid IS NOT NULL and prin.sid NOT IN (0x00)
and prin.is_fixed_role <> 1 AND prin.name NOT LIKE ''##%'''

SELECT dbname, username, logintype, create_date, modify_date,
    STUFF((SELECT ',' + CONVERT(VARCHAR(500), associatedrole)
        FROM @DB_Users user2
        WHERE user1.DBName=user2.DBName AND user1.UserName=user2.UserName
        FOR XML PATH('')
    ),1,1,'') AS Permissions_user
FROM @DB_Users user1
WHERE user1.UserName = N'<put your login-name here!>'
GROUP BY dbname, username, logintype, create_date, modify_date
ORDER BY DBName, username
NateJ
fonte
-1

A Consulta abaixo deve retornar os mapeamentos para o DbName solicitado

SELECT 'DbName', dbPri.name, dbPri1.name
FROM [DbName].sys.database_principals dbPri 
JOIN [DbName].sys.database_role_members dbRoleMem ON dbRoleMem.member_principal_id = 
dbPri.principal_id
JOIN [DbName].sys.database_principals dbPri1  ON dbPri1.principal_id = 
dbRoleMem.role_principal_id
WHERE dbPri.name != 'dbo'

Consulta aprimorada abaixo

declare @sql varchar(Max)

 set @sql = 'use ? SELECT ''?'', dbPri.name, dbPri1.name
 FROM sys.database_principals dbPri 
 JOIN sys.database_role_members dbRoleMem ON 
 dbRoleMem.member_principal_id = 
 dbPri.principal_id
 JOIN sys.database_principals dbPri1  ON dbPri1.principal_id = 
 dbRoleMem.role_principal_id
 WHERE dbPri.name != ''dbo'''

 EXEC sp_MSforeachdb @sql
dilipkumar katre
fonte
Obrigado, o objetivo desse script era encontrar um mapeamento para todos os bancos de dados. Seu script fornece informações para apenas um banco de dados específico, nem todos eles.
precisa
Obrigado pelo feedback, pensei que pode ser repetido. Agora atualizado com consulta aprimorada
dilipkumar katre
Veja a resposta de @ Aaron-Bertrand e comente seus pensamentos sobre sp_MSforeachdb.
precisa
-3

Que tal EXEC master..sp_msloginmappings?

Nacho
fonte
Você sp_msloginmappingschecou se está sem documentos e sem suporte antes da postagem?
Kin Shah