繁体   English   中英

授权数据库级别的数据访问

[英]Authorize data access at database level

从问题标题中,您可能会猜到这是什么意思。 我将尝试描述当前拥有的内容以及要存档的内容。

假设一个应用程序处理四个实体: UserTeamRepositoryDocument 这些实体之间的关系是:

  • 每个用户属于零个或多个团队
  • 每个文档都属于一个存储库
  • 用户可能拥有零个或多个存储库
  • 每个存储库都可以创建为公共私有
  • 与存储库所有者共享团队的所有用户都可以看到公共存储库的内容。
  • 私有存储库仅对其所有者可见。

访问用户的文档不是问题,这些都是存储在他拥有的存储库中的所有文档。 但是事情变得复杂了,因为我真正需要的是用户可见的所有文档,这就是所有文档以及其他人公开并与他共享团队的文档。

目前,我正在数据访问层中强制执行此授权机制。 这意味着要获取所有文档并按照上述规则进行一些过滤。 我知道此实现无法扩展,并且我想知道是否可以通过将授权逻辑移至数据库来改善数据库模型。 这样,过滤将由数据库引擎完成,并且只有请求的实体将返回给客户端代码。

这个问题与特定的实现无关,但我将其标记为正在使用的特定工具。 也许对某人的回答很有用。

首先,让我解释一下为什么使用实体框架(或另一个ORM工具)比使用存储过程更优雅。

存储过程是邪恶的 这就是为什么。 正如该链接详细解释的那样,存储过程倾向于作为第二个BL增长,因此难以维护。 当该列在多个存储过程中使用时,重命名列这一简单任务将成为一项艰巨的任务。 当您使用ORM工具时,Visual Studio将为您完成大部分工作。

话虽如此,但我却获得了实体框架的第二个优势。 您可以使用自己喜欢的.net语言来组成查询。 实体框架不会直接执行查询。 您可以在此处控制查询何时执行查询。 执行此实体框架时,会将您的Linq语句编译为完整的tsql语句,并针对数据库运行该语句。 因此,绝对不需要获取所有数据并遍历每个记录。

提示:将光标移到变量名上,ef将为您预览将要编译的TSQL语句。

那么您的Linq查询应该如何? 我根据您的描述组成了一个测试数据库,并为其创建了一个实体框架(ef6)模型,如下所示:

EF模型

至少在我正确理解您的问题的前提下,此Linq查询将执行您想要的操作。

private IEnumerable<Document> GetDocumentsOfUser(Guid userId)
    {
        using (var db = new DocumentRepositoryEntities())
        {
            // Get owned repositories by the user
            var ownedRepositories = db.Repositories
                          .Where(r => r.Owner.UserId == userId);

            // Get all users of teams the user belongs to
            var userInOtherTeams =
                 db.Users.Where(u => u.UserId == userId)
                        .SelectMany(u => u.Teams)
                        .SelectMany(t => t.Users);

            // Get the public repositories owned by the teammembers
            var repositoriesOwnedByTeamMembers =
                userInOtherTeams.Where(u => u.Repositories.Any())
                                .SelectMany(u => u.Repositories)
                                .Where(r => !r.Private);

            // Combine (union) the 2 lists of repositories
            var allRepositories = ownedRepositories.Concat(
                                  repositoriesOwnedByTeamMembers);

            // Get all the documents from the selected repositories
            return allRepositories.SelectMany(r => r.Documents)
                                  .Distinct()
                                  .ToArray(); //query will be composed here!
        }
    }

请注意,在调用.ToArray()时,linq语句将被编译为TSQL select语句。

根据您的描述,目标是找到用户当前有权访问的所有存储库,然后从每个存储库中检索文档。

如果这是我的实现,则需要向数据库中添加一个接受当前用户ID的存储过程,然后将可访问存储库列表收集到本地表变量中,然后从documents表中选择文档存储库所在的位置。可访问的存储库列表。

DECLARE
  @Teams TABLE (TeamID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY (TeamID))

DECLARE
  @Repositories TABLE (RepositoryID UNIQUEIDENTIFIER NOT NULL PRIMARY KEY (RepositoryID))

  /* Get the list of teams the user is a member of */
INSERT INTO @Teams
SELECT Teams.TeamID
  FROM Teams INNER JOIN TeamUsers ON Teams.ID = TeamUsers.TeamID
 WHERE TeamUsers.UserID = @UserID

  /* Get the list of repositories the user shares a team member with */
INSERT INTO @Repositories
SELECT RepositoryID
  FROM Repositories
 WHERE OwnerID = @UserID 
    OR (OwnerID IN (SELECT DISTINCT TeamUsers.UserID
                     FROM TeamUsers INNER JOIN @Teams ON TeamUsers.TeamID = @Teams.TeamID)
       AND IsShared = 1)

  /* Finally, retrieve the documents in the specified repositories */
SELECT Documents.* 
  FROM Documents INNER JOIN @Repositories ON Documents.RepositoryID = @Repositories.RepositoryID

主管技术建议的答案是有效的,如果您的需求是一次性的,那么这很好,但理想情况下,您想要做的是在专用层中以外部化方式实现授权要求。 这样做的原因包括:

  • 易于维护分离的架构
  • 您可以不修改应用程序和/或数据库就更新授权
  • 您不需要SQL /存储过程知识
  • 您可以更轻松地报告在什么地方应用了什么授权:如果审核员喘口气,这一点很重要。

要实现外部授权 (有关此主题的Gartner报告 ,请参见此处),您需要考虑基于属性的访问控制(ABAC- 有关NIST关于ABAC的报告,请参见此处 )和可扩展访问控制标记语言(XACML)- 更多信息此处 )作为实施ABAC的一种方式。

如果遵循ABAC方法,您将获得:

  • 具有以下概念的干净,分离的架构
    • 位于应用程序和数据库之间的强制执行点或拦截器(如果ABAC应用于数据库)
    • 授权决策引擎,该决策引擎将做出决策,并将生成一个过滤器语句(对于SQL数据库,是WHERE子句),强制执行点将附加到原始SQL语句中
  • 一个基于策略和基于属性的授权模型,通过该模型,您可以在易于理解的语句中编写授权需求,而不是过程,PL-SQL或其他SQL构件。 示例包括:
    • *用户可以编辑自己拥有的文档
    • 如果用户团队==文档团队,则用户可以查看文档
    • 当且仅当文件被标记为公开时,用户才能查看另一个团队的文件
    • 具有角色编辑器的用户可以并且仅当文档状态为草稿*时才能编辑属于其团队的文档

在上述示例中,用户类型,资源类型(文档),操作(查看,编辑),文档团队,用户团队和文档可见性(私有或公共)都是属性的示例。 属性是生命线,ABAC的基础。

ABAC可以轻松地帮助您实现从最简单的要求到更高级的授权要求(例如可以在出口法规,合规性法规或其他业务规则中找到)。

这种方法的一个好处是它并不特定于数据库。 您可以将相同的原理和策略应用于本地开发的应用程序,API,Web服务等。 这就是我所谓的外部授权的任何深度架构/方法。 下图很好地总结了它:

任意深度的架构和外部授权

PDP是您的集中式授权引擎。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM