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

时间:2014-10-06 20:20:58

标签: c# database-design asp.net-mvc-5 authorization entity-framework-6

从问题标题中你可能会猜到这是什么。我将尝试描述我目前拥有的和我想要存档的内容。

假设一个应用程序处理四个实体:用户团队存储库文档。这些实体之间的关系是:

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

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

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

这个问题与特定的实现没有关系,但我会为我正在使用的特定工具标记它。也许这对某人的答案很有用。

3 个答案:

答案 0 :(得分:3)

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

Stored Procedures are evil。这就是原因。正如链接详细解释的那样,存储过程往往会增长为第二个BL,因此难以维护。当在多个存储过程中使用此列时,重命名列的简单任务将成为一项重大任务。当您使用ORM工具时,visual studio将为您完成大部分工作。

这就说明了实体框架的第二个优势。您可以使用自己喜欢的.net语言撰写查询。实体框架不会直接执行您的查询。您可以控制何时执行查询,因为您可以阅读here。执行此实体框架时,会将Linq语句编译为完整的tsql语句,并针对数据库运行此语句。因此,绝对不需要获取所有数据并循环遍历每条记录。

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

那么你的Linq查询应该怎么样?我根据你的描述编写了一个测试数据库,并建立了一个实体框架(ef6)模型,它看起来像:

EF model

这个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语句。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

虽然答案competent_tech表明是有效的,如果您的需求是一次性的,那么您的理想选择是在外部化的专用层中实施您的授权要求。这样做的原因包括:

  • 更容易维护解耦架构
  • 您可以在不联系您的应用程序和/或数据库的情况下更新授权
  • 您不需要SQL /存储过程知识
  • 您可以更轻松地报告授权适用的地方:如果您让审核员喘息,这很重要。

要实现外化授权(请参阅此处有关该主题的Gartner report),您需要考虑基于属性的访问控制(ABAC - see here for a report on ABAC by NIST)和可扩展性访问控制标记语言(XACML - more info here)作为实施ABAC的手段。

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

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

在上面的示例中,用户类型,资源类型(文档),操作(视图,编辑),文档的团队,用户的团队以及文档的可见性(私有或公共)都是属性的示例。属性是生命线,是ABAC的基石。

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

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

The any-depth architecture and externalized authorization

PDP是您的集中授权引擎。