Une requête qui répertorie tous les utilisateurs mappés pour une connexion donnée

19

Lorsque vous regardez les propriétés d'une connexion particulière, il est possible de voir une liste d'utilisateurs mappés à cette connexion: entrez la description de l'image ici

J'ai profilé SQL Server Management Studio (SSMS) et je vois que SSMS se connecte à chaque base de données une par une et récupère les informations de sys.database_permissions

Est-il possible d'écrire une seule requête qui récupère les informations de mappage utilisateur indiquées ci-dessus ou suis-je obligé d'utiliser un curseur ou sp_MSforeachdb ou quelque chose comme ça?

Michael J Swart
la source
5
Veuillez ne pas utiliser sp_MSforeachdb .
Aaron Bertrand

Réponses:

15

Voici une façon d'utiliser le SQL dynamique. Il n'y a pas vraiment de moyen de le faire sans itérer, mais cette approche est beaucoup plus sûre que les options non documentées, non prises en charge et boguéessp_MSforeachdb .

Cela obtiendra une liste de toutes les bases de données en ligne, l'utilisateur mappé (s'il existe) avec le nom de schéma par défaut et une liste séparée par des virgules des rôles auxquels ils appartiennent.

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
la source
1
note intéressante, j'ai dû ajouter des classements explicites aux colonnes p.name et p.default_schema_name pour que l'union fonctionne correctement
Michael J Swart
@MichaelJSwart Ah oui, je l'ai déjà rencontré lorsque les bases de données ont des classements différents (certaines colonnes de métadonnées utilisent le classement du serveur mais d'autres héritent du classement de la base de données). J'espère que les seules personnes qui brûlent vraiment sont celles qui insistent pour utiliser des caractères farfelus dans les noms d'entités qui ne sont pris en charge que dans un classement obscur ...
Aaron Bertrand
7

Ce script est légèrement modifié à partir d'un script mentionné à fera ce que vous cherchez. Remplacez «ThursdayClass» par la connexion dont vous avez besoin d'informations. 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
la source
Merci Taiob, cela fonctionne bien (je mettrais la colonne database_name entre crochets ('[' et ']')
Michael J Swart
5

Essayez sp_dbpermissions . Il vous donnera probablement plus d'informations que vous n'en avez besoin, mais il fera ce que vous voulez.

Une fois installé, exécutez ceci.

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

Juste avertissement pour le moment, il fait une correspondance "J'aime", donc si d'autres connexions sont similaires et correspondent, vous les verrez également. Par exemple, MyLoginet MyLoginForThiscorrespondront tous les deux MyLogin. Si c'est un problème, j'ai une version que je n'ai pas encore publiée où vous pouvez la désactiver. Faites le moi savoir et je pourrai vous l'envoyer par e-mail.

Kenneth Fisher
la source
4

Voici une solution 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
la source
4

Malheureusement, vous devrez parcourir toutes les bases de données pour obtenir les informations. Vous voudrez vous joindre sys.database_principalsà sys.server_principalschaque base de données correspondant sur le SID.

Ne l'utilisez pas sp_msforeachdbcar il est connu de manquer des bases de données à certains moments.

Nic
la source
1

Je cherchais une réponse similaire et j'ai trouvé ceci: https://www.pythian.com/blog/httpconsultingblogs-emc-comjamiethomsonarchive20070209sql-server-2005_3a00_-view-all-permissions-_2800_2_2900_-aspx/ . Et oui, il utilise le redouté sp_MSforeachDB, mais je pense que ce gars obtient parfois un mauvais rap ... ;-)

Je posterai le SQL ici pour des pâtes de copie faciles (je ne prends pas le crédit pour cela, je le rend facilement accessible!):

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
la source
-1

Ci-dessous, la requête doit renvoyer les mappages pour le DbName demandé.

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'

La requête améliorée est ci-dessous

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
la source
Merci, l'objectif de ce script était de trouver un mappage pour toutes les bases de données. Votre script fournit des informations pour une seule base de données spécifique, pas toutes.
Michael J Swart
Merci pour les commentaires, je pensais que cela pouvait être répété. Maintenant mis à jour avec une requête améliorée
dilipkumar katre
Voir la réponse de @ Aaron-Bertrand et commenter ses réflexions sur sp_MSforeachdb.
Michael J Swart
-3

Et alors EXEC master..sp_msloginmappings?

Nacho
la source
Avez-vous vérifié si elle sp_msloginmappingsétait sans papiers et sans support avant de poster?
Kin Shah