tsql - 通过SQL递归查询AD组成员身份

标签 tsql active-directory sql-server-2008-r2 adsi ldap-query

背景

我正在创建一些 SQL 来协助安全审计;这将从各种系统数据库和 Active Directory 获取安全信息,并将生成所有异常的列表(即帐户在一个系统中关闭而不是其他系统中的情况。

当前代码

要获取属于安全组成员的用户列表,我运行以下 SQL:

if not exists(select 1 from sys.servers where name = 'ADSI') 
    EXEC sp_addlinkedserver 'ADSI', 'Active Directory Services 2.5', 'ADSDSOObject', 'adsdatasource'

SELECT sAMAccountName, displayName, givenName, sn, isDeleted --, lastLogonTimestamp --, lastLogon (Could not convert the data value due to reasons other than sign mismatch or overflow.)
FROM OPENQUERY(ADSI
, 'SELECT sAMAccountName, displayName, givenName, sn, isDeleted
FROM ''LDAP://DC=myDomain,DC=myCompany,DC=com''
WHERE objectCategory = ''Person''
AND objectClass = ''user'' 
AND memberOf = ''CN=mySecurityGroup,OU=Security Groups,OU=UK,DC=myDomain,DC=myCompany,DC=com''
')
order by sAMAccountName

问题/疑问

我希望这段代码能够递归地工作;即,如果用户是指定组成员的组的成员,则也应包括他们(对于完整层次结构)。有谁知道如何通过 SQL 做到这一点?

更新

我现在已经解决了一些问题(与引用的问题无关,而是我遇到的其他一些问题)。
  • lastLogon 抛出错误。这是因为服务器版本是 x86。使用 x64 数据库解决了这个问题。
  • lastLogon 以数字形式返回。添加了一些代码以将其转换为 DateTime2。
  • 通过使 OpenQuery 本身动态化,我能够将组名从硬编码字符串中移出,因此在 OpenQuery 的上下文中,生成的字符串看起来是静态的。

  • ..
    --create linked server
    if not exists(select 1 from sys.servers where name = 'ADSI')
    begin
        --EXEC sp_addlinkedserver 'ADSI', 'Active Directory Services 2.5', 'ADSDSOObject', 'adsdatasource'
        EXEC master.dbo.sp_addlinkedserver 'ADSI', 'Active Directory Service Interfaces', 'ADSDSOObject', 'adsdatasource'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'collation compatible',  @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'data access', @optvalue=N'true'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'dist', @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'pub', @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'rpc', @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'rpc out', @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'sub', @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'connect timeout', @optvalue=N'0'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'collation name', @optvalue=null
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'lazy schema validation',  @optvalue=N'false'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'query timeout', @optvalue=N'0'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'use remote collation',  @optvalue=N'true'
        EXEC master.dbo.sp_serveroption @server=N'ADSI', @optname=N'remote proc transaction promotion', @optvalue=N'true'
    end
    
    
    declare @path nvarchar(1024) = 'DC=myDomain,DC=myCompany,DC=com'
    declare @groupCN nvarchar(1024) = 'CN=My Security Group,OU=Security Groups,OU=UK,' + @path
    , @sql nvarchar(max)
    
    --construct the query we send to AD
    set @sql = '
    SELECT sAMAccountName, displayName, givenName, sn, isDeleted, lastLogon
    FROM ''LDAP://' + replace(@path,'''','''''') + '''
    WHERE objectCategory = ''Person''
    AND objectClass = ''user'' 
    AND memberOf = ''' + replace(@groupCN,'''','''''') + '''
    '
    
    --now wrap that query in the outer query
    set @sql = 'SELECT sAMAccountName, displayName, givenName, sn, isDeleted
    , case     
        when cast([lastLogon] as bigint) = 0 then null
        else dateadd(mi,(cast([lastlogon] as bigint) / 600000000), cast(''1601-01-01'' as datetime2)) 
      end LastLogon
    FROM OPENQUERY(ADSI, ''' + replace(@sql,'''','''''') + ''')
    order by sAMAccountName'
    
    --now run it
    exec(@sql)
    

    最佳答案

    尽管这是一篇旧帖子,但 Google 仍然喜欢将其放在结果的顶部,因此当我在同一问题上苦苦挣扎时,我想发布我的发现/解决方案,感谢 Riverway 让我参与正确的轨道上。

    创建存储过程:

    CREATE PROCEDURE [dbo].[GetLdapUserGroups]
        (
        @LdapUsername NVARCHAR(max)
        )
    AS
    BEGIN
    DECLARE @Query NVARCHAR(max), @Path NVARCHAR(max)
    
    SET @Query = '
        SELECT @Path = distinguishedName
        FROM OPENQUERY(ADSI, ''
            SELECT distinguishedName 
            FROM ''''LDAP://DC=DOMAIN,DC=COM''''
            WHERE 
                objectClass = ''''user'''' AND
                sAMAccountName = ''''' + @LdapUsername + '''''
        '')
    '
    
    EXEC SP_EXECUTESQL @Query, N'@Path NVARCHAR(max) OUTPUT', @Path = @Path OUTPUT 
    
      SET @Query = '
        SELECT cn AS [LdapGroup]
        FROM OPENQUERY (ADSI, ''<LDAP://DOMAIN.COM>;
        (&(objectClass=group)(member:1.2.840.113556.1.4.1941:= ' + @Path + '));
        cn, adspath;subtree'')
        ORDER BY cn;
    '
    
    EXEC SP_EXECUTESQL @Query
    END
    

    然后,只需传递用户名即可调用您的 SP:
    DECLARE @UserGroup table (LdapGroup nvarchar(max))
    INSERT INTO @UserGroup exec Datamart.dbo.GetLdapUserGroups @LdapUser
    

    然后我使用哈希表将 AD 组与 SQL 数据以及最终用户应该看到的内容正确匹配。
    DECLARE @RptPermissions table (ldapGroup nvarchar(max),scholarshipCode nvarchar(50),gender nvarchar(2))
    INSERT INTO @RptPermissions VALUES('EMP_Enrollment_Admissions','ALL','MF')
    

    就我而言,我使用它来拉取 SSRS 用户变量并将其传递到查询中以根据 AD 组成员身份选择记录。
    ;WITH CTE_Permissions AS
    (
        SELECT
            p.scholarshipCode
            ,p.gender
        FROM @UserGroup AS g
        JOIN @RptPermissions AS p ON
            g.ldapGroup = p.ldapGroup
    )
    

    ...稍后在查询中
    JOIN CTE_Permissions AS p ON
             s.SCHOLARSHIP_ID = p.scholarshipCode
             OR p.scholarshipCode = 'ALL'
    

    希望这可以帮助。

    关于tsql - 通过SQL递归查询AD组成员身份,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13914698/

    相关文章:

    SQL 获取销售额超过一定数量的商品数量

    sql-server - SQL Server : why doesn't this query return NULL values?

    sql-server - T-SQL 中的一些内容 : how to use it?

    active-directory - 使用 rfc2254 中指定的 objectGUID 编码的事件目录过滤器不起作用

    sql-server - 在 SQL 2008/12 中压缩 VARCHAR - 看不到结果

    sql - 从表中选择前 N 行 Column containing N

    sql - 获取存储字符的 ASCII 值的 T-SQL 函数

    active-directory - 如何在System.DirectoryServices.Protocols中放弃长时间运行的搜索

    java - 如何通过java编程检索Active Directory中的属性 "unicodePwd"

    sql - 如何使用 SQL Server 连接选择中的所有列