我该如何在关系型数据库中建模(类似GitHub的)权限关系?

11
TL;DR:我如何实现类似GitHub的权限模型?
更新以尝试解决@philipxy的一些评论:
我计划实现类似于GitHub的权限模型。
  • 用户
  • 用户可以分组
  • 用户可以加入组织
  • 组可以加入组织
  • 一个用户将被允许在资产、组或组织上执行任何 C、R、U 和 D 操作,如下所述:
    1. 已被授权执行这些(任何 C、R、U、D)操作的个人用户
    2. 已被授予权限的组的成员
    3. 已被授予权限的组织的成员
      1. 或作为属于具有权限的组织的组的成员
  • 用户被授予读取权限,因为资产/组/组织对匿名用户(“公共”)可见(可读取)
  • 用户还应该拥有一组权限,以说明他们是否可以在权限上执行任何 C、R、U 或 D 操作(用户可以为另一个用户、组或组织创建一个权限[C、R、U、D])。
    1. 用户可以设置他们创建的任何资产、组或组织的权限,或者任何他们已被授予设置权限的资产、组或组织的权限。
这些权限将控制谁可以在网站上执行资产、组和组织的创建、读取、更新和删除(CRUD)操作。
大致上,我该如何建模?
显然,我有以下模型:
1. 资产 2. 用户 3. 组 4. 组织
接下来呢?
1. 权限? 2. 权限类型(用于捕获C/R/U/D)?
我正在使用通过sequelize从node连接的mysql,但我可以自己弄清楚具体语法等问题,我只是还没有概念性地弄清楚如何做到这一点。
更进一步:
您提议我更多地做的事情,正是我认为需要帮助的事情。也就是说,我正在寻找那些信息设计方法(NIAM、FCO-IM、ORM2、IDEF1X)。我对关系数据库实现了解得相当多(学习规范化和标准化等),但确实将业务需求规范化并转换为可行规格的过程是一个挑战。
  • ORM2 因与nodejs模块名称冲突而难以找到。 : 我已经从NIAM维基百科页面链接下载了相关书籍。
  • 现在似乎使用NIAM的人越来越少了?
  • FCO-IM:我已经从他们的网站下载了相关书籍。
  • IDEF1X:看起来也很有趣。

我想我要开始学习数据库的教材了。

更多关于谓词的工作:

  • U表示一个用户
  • A表示一个资产
  • G表示一个群组
  • 一个用户 U 可以在 0 或多个群组 G
  • O 表示一个组织
  • 一个用户 U 可以在 0 或多个组织 O
  • 一个群组 G 可以在 0 或多个组织 O
  • 一个资产 A 可以由一个用户 U 创建
  • 对于 Assets 的 CRUD 操作:
    1. 一个实体 E 可以通过权限 P 被允许执行操作 AcAssets
    2. 这些操作包括:
      1. Create
      2. Read
      3. Update
      4. Delete
    3. 该实体可以是以下类型之一:
      1. User
      2. Group
      3. Organization
      4. Anonymous User/"the public"
    4. 细节(仅在 Read 中显示,但也与 CreateUpdateDelete 相关):
      1. 一个用户 U0 可以允许另一个用户 U1 读取资产 A
      2. 一个用户 U0 可以允许属于群组 GUsers U 读取资产 A
      3. 一个用户 U0 可以允许属于组织 OUsers U 读取资产 A
      4. 在已被允许读取资产 A 的组织 O 中的 Group G1 中的 Users U 因此可以读取资产 A
  • 引用资产 A 的权限 P 只能由某些用户创建:
    1. 默认情况下,创建实体的用户 U 可以为该实体创建 Permissions
    2. 但是他们只能引用他们拥有权限的 Assets(在基本情况下:由 U 创建的那些 Assets
    3. 已被授予权限的用户也可以引用实体 E 中的权限
    4. Gr 表示一个授权
      1. 一个授权赋予一个实体创建、读取、更新或删除引用另一个实体的权限的特权
      2. 像权限一样,授权具有传递性质:
        1. 如果组织 O 已被授予修改实体 E 的权限,则
        2. 不仅属于 O 的成员的用户可以修改引用 E 的权限,
          1. 而且任何群组 G,其中

1
你的扩展语言仍然不够清晰。例如,你添加了“用户将被允许执行C、R、U和D操作”,但这仍然存在着我所描述的1对4个“权限”的歧义。你需要开始明确“权限”是什么,以及实体何时具有特定权限来执行特定操作(等等),否则你的问题中就没有“细节”可以提取。另外,你的问题到底是什么,因此答案会包含什么内容也不是很清楚。顺便说一下,要注意翻译的准确性,避免机器翻译的风格。 - philipxy
我仍然不确定如何澄清。很抱歉。这是一个尝试:我不知道我应该有1个还是4个权限。我想能够分别决定用户是否可以创建资产,用户是否可以读取资产,他们是否可以编辑资产以及是否可以删除资产。我还想能够分别指定用户是否可以创建组,是否可以编辑组...所以我猜是4个单独的事情?虽然也许C、R、U、D只是权限的属性? - Michael
@philipxy 我在最近的更新中是否接近了你所推崇的特定级别? - Michael
如果您对数据库建模有兴趣,特别是IDEF1X或IE符号表示法,可以查看ERwin建模指南 - Branko Dimitrijevic
看起来 GitLab(开源,Ruby on Rails)使用了类似的模型。因此,也许查看他们的源代码可以帮助(我和)其他人尝试做到这一点。 - Michael
显示剩余2条评论
3个回答

7

谓词和表格

命题是关于业务情况的真假陈述。谓词是一个带有列参数的语句,在给定一行的情况下,可以得出一个命题。一个表(基础表或查询结果)保存了满足其谓词的行,使得该命题为真。

user (with id) U has name N
R is a grantor (may grant permissions)

user U has permission to update asset A
grantor R gave permission to grantor E to use an operator of type 'CRUD'
grantor E is of type 'user' AND grantor E has permission to update assets

业务规则

业务规则是一种始终成立的声明,用于定义术语或描述政策或流程。

A user is uniquely identified by an id assigned when their cheque clears.  
A crudable is an asset, group or organization.  
A grantor is a user, group, organization.
"Grantee" refers to a grantor receiving or holding a permission.    
Users can be in organizations.  

您可以创建无需参数的谓词来表达真实陈述。这些谓词可以使用被“对于所有”(FOR ALL)和“对于某些”(FOR SOME)(THERE EXISTS)绑定的参数名称。以此类命题谓词和/或表名作为业务规则的规范,称为数据库约束。假设User(U,N)Grantor(R)是上述两个谓词的简写,用于表示表UserGrantor中的谓词,则以下各行具有相同的含义:
A user is a grantor.
FOR ALL U, if U is a user then U is a grantor.

FOR ALL U, (FOR SOME N, User(U, N)) IMPLIES Grantor(U).
(SELECT U FROM User) ⊆ (SELECT R AS U FROM Grantor).

FOR ALL U & N, User(U, N) IMPLIES Grantor(U).
FOR ALL U & N, (U, N) IN User IMPLIES (U) IN Grantor.

FOREIGN KEY User (U) REFERENCES Grantor (R);这句话表明了上述内容(请注意其与中间两个内容的相似之处),并且在Grantor中R是唯一且不能为空。

不要混淆规则和谓词。它们有不同的用途,通常有不同的形式。(一个无参数的句子模板可以用作任何一种。)规则是真实的陈述;谓词是有参数的陈述。看看我的答案如何将它们分开。基本表和查询结果表都有谓词。但是规则可能暗示您需要一个基本谓词/表来记录某些情况。当我们从规则中看到需要记录某些关于当前情况的声明时,我们就有了基本谓词/表。请注意,一些规则没有启发基本谓词。

您可能想要“具体化”类型和权限。

A user is a grantor of type 'user'.
Permission named 'C' is permission for a grantee to create a crudable.

Grantor E is of type 'user'.
Permission P is of type 'CRUD'.
Grantor R gave permission P of type 'CRUD' on crudable C to grantee E.

设计就是找到必要且充分的规则和基本前提

以下是相关前提,用于记录您所暗示的情况。

  1. 用户
U identifies a user
  1. 用户可以分组
G identifies a group
user U is in group G
  1. 用户可以在组织中
O identifies an organization
user U is in organization O
  1. 组可以属于组织
group G is in organization O
  1. 用户将被允许对资产、组织或组进行CRUD操作。
A identifies a crudable of type 'asset'
user U is permitted CRUD operations on crudable C

作为个人用户、团队成员或组织成员(或作为一个所属组织有权限的小组成员),
P identifies a permission
organization O is permitted CRUD operations on crudable C

或者因为资产/组织机构对匿名用户(“公共”)是可视的(可读的)。
crudable C is public
  • 用户还应拥有一组权限,以确定他们是否能够设置上述权限
  • grantor R has permission to set CRUD permission for users on crudable C --?  
    

    “上述权限”是什么?也许你指的是用户CRUD权限和组织CRUD权限?也许你是指有个别操作(创建、读取等)的权限?你需要更明确一些。
    “一组权限”中的权限是什么?这里的“权限”是否真的意味着“特定授权对象的特定权限”?你需要更加明确一些。
    要更清晰地表达,可以给出尽可能简单但同时不会忽略相关实体/值的规则和谓词。之后,您可能还需要将多个规则和谓词概括为单个规则和谓词。例如,不是处理用户、组织和资产,而是使用授权者和可CRUD对象:「授权者可以授予权限」和「授权者R向授权对象E授予权限P」。如果某些权限也与特定的授权对象相关联,则可能还需要像「授权者R向授权对象E和F授予权限P和Q」这样的谓词。

    6.1. 用户可以为他们创建的任何资产、组或组织设置权限。

    user U created crudable C
    

    或任何已被授权设置权限的资产、组或组织。
    user U has permission to set permission P for crudable C --?  
    

    你需要记录像用户U有姓名N和...这样的内容。

    了解数据库设计

    搜索关于数据库/SQL子类型、继承/多态范例的相关信息。例如,用户、组和组织是权限所有者和持有者的类型;我将它们作为授权者的子类型。也许你需要某种权限目标类型,它是crudable和授权者的并集,也许你需要权限类型。也许有些权限权限与受让人相关联。也许‘C’、‘R’、‘U’和‘D’是权限,“CRUD”是一种权限类型。你可能要记录授权者向被授权人授予了什么权限。

    稍后,如果连接在具有相同值集的共享PK/UNIQUE上,我们可以用连接替换表格。当我们可以在PK/UNIQUE和FK上连接时,我们可以用一个带有FK可空性的连接替换表格。还有其他时候,我们可以无问题地用一个表格代替多个表格。但首先要确定基本谓词。

    了解关系数据库设计。遵循一些信息设计方法。最好的是NIAM/FCO-IM/ORM2家族的成员。看看IDEF1X。不要依赖产品。

    了解约束条件。它们是从谓词和业务规则中推导出来的。它们是关于谓词可能业务情境的真实性。同样地,它们是关于表格可能数据库状态的真实性。还要了解SQL中的约束条件,包括申明式的(PK、UNIQUE、FK)和触发式的。


    谢谢!我已经尝试更新我的问题以解决您的一些顾虑。我也开始寻找信息设计资源。 - Michael

    0

    我认为您需要创建一个实体的概念,该实体可以在存储库或组织上接收权限。

    在这个模型中,问题变得相对简单,因为权限将围绕着一个形式为RepositoryPermissions表的实体展开:

    (EntityId, RepositoryId, canCreate, canRead, canUpdate, canDelete)
    

    还有一个形式为OrganizationPermissions的表:

    (EntityId, OrganizationId, canCreate, canRead, canUpdate, canDelete)
    

    有两种类型的实体,即用户,任何用户的权限都需要通过以下四种方式进行检查:

    • 直接在仓库级别上设置的用户权限
    • 在具有仓库级别权限的中的用户成员身份
    • 直接在组织级别上设置的用户权限
    • 在具有组织级别权限的中的用户成员身份

    以下查询应检索与用户someUserId在仓库someRepoId上相关的所有权限条目。

        SELECT
          rp.canCreate,
          rp.canRead,
          rp.canWrite,
          rp.canDelete
        FROM RepositoryPermissions AS rp
    
        Left JOIN Users      AS u  ON u.EntityId = rp.EntityId
        Left JOIN Groups     AS g  ON g.EntityId = rp.EntityId
        Left JOIN GroupUsers AS gu ON gu.GroupId = g.GroupId
    
        WHERE rp.repositoryId IS "someRepoId" AND (
          u.UserId IS "someUserId" OR
          gu.UserId IS "someUserId"
        )
    UNION
        SELECT 
          op.canCreate,
          op.canRead,
          op.canWrite,
          op.canDelete
        FROM Repositories AS r
        JOIN OrganizationPermissions AS op ON r.repositoryId = op.repositoryId
    
        Left JOIN Users      AS u  ON u.EntityId = op.EntityId
        Left JOIN Groups     AS g  ON g.EntityId = op.EntityId
        Left JOIN GroupUsers AS gu ON gu.GroupId = g.GroupId
    
        WHERE r.repositoryId IS "someRepoId" AND (
          u.UserId IS "someUserId" OR
          gu.UserID IS "someUserID"
        )
    

    感谢您抽出时间写下这些内容!但是,如果我理解您的回答:(根据我正在尝试解决的问题:)如果用户U1在组G1中,并且允许组G1读取存储库R1,则可以通过传递性认为U1能够读取R1,但我认为这不会通过您建议的查询实现?(因此,我已经对您的回答进行了投票,以防其他读者看到它并误解我试图捕捉的复杂性)。 - Michael
    @Michael,我已经更新了查询,考虑到更多情况而不仅仅是我展示的一个案例。我相信这个查询应该符合你的要求。 - Mobius

    -3

    当你试图让数据库像Github一样运作时,这是有问题的。数据库有完全不同的范例。这就是为什么基于C#的设计数据库也同样糟糕。它们没有相同的功能或规则。然而,这是一个有趣的问题,因为太少的人将权限模型作为数据库设计的一部分来关注,他们只是授予每个人所有权限,因为这更容易。

    首先,用户不应该能够授予权限给其他人,只有管理员才能这样做。如果你有一个良好的数据库设计,用户永远不应该创建对象。数据库设计不应该由业余人员处理。

    用户可以是个人,或者你可以使用应用程序用户,所有来自特定应用程序的数据库请求都具有相同的权限。应用程序甚至可以拥有多个通用用户,例如XYZAdmin、XYZUser、XYZReadonly(通常用于需要查看数据但不会进行调整的高级经理)。从我的经验来看,通用用户的最大问题在于,在受监管的环境中,很难审计谁更改了数据库中的什么内容,并且一些用户可能拥有比他们实际需要的更多权限。

    在数据库中,您有几种基本类型的权限。您拥有扩展到整个数据库和对象级别权限的权限。您还拥有一些特定的服务器权限,例如使用批量插入或执行作业的权限。应该给组授予关于它们可以在服务器上看到哪些数据库的权限,因为大多数数据库服务器都有多个数据库。因此,您可以授予用户对任何表进行写入的权限,或者仅授予他们对特定存储过程或视图的权限而不授予表的任何权限。通常,管理人员获得对所有内容(或服务器级别的权限,如批量插入权限)的总体权限。这包括具有完全权限的DBA以及其他专家,例如数据分析师/高级开发人员/构建团队成员,他们可能只具有关于作业和数据输入或创建新对象的权限,但没有执行设置权限等任务的权限。所有其他用户应该被锁定到对象级别的权限。

    现在不应该将权限授予个人(或者至少应该很少这样做)。每个人都应该属于各种具有权限的组。个人可以属于多个组,如何在组之间交互权限可以在与您使用的特定数据库产品相关联的文档中解释(是的,这是特定于产品的,没有一种适用于所有数据库权限模型)。美妙之处在于,当某个人不再是用户时,您只需要将他们从组中删除,权限就会消失,而不是寻找大量的个人权限。

    现在,当您通过组处理权限时,您需要定义这些组。如果您希望组仅对记录或列的子集具有权限而不是整个表,则可能还需要定义一些附加视图或存储过程。如果您在一个数据库中有多个客户端,并且需要确保权限仅针对一个特定的客户端,则需要使用存储过程/视图,并仅在存储过程/视图级别而不是表级别授予权限。使用视图进行权限管理可能会变得棘手,因为每个数据库产品都有特定的规则来确定哪些视图可以更新。您需要深入了解这一点,以确定如何管理权限。

    所有创建的数据库对象都需要在创建脚本中编写授予权限的特定组。 (您永远不会通过任何GUI创建数据库对象,而是始终通过保存在源代码控制中的脚本进行创建。)

    您还可以通过设置一些包含有关用户可以访问的特定页面或允许其访问的特定客户端的元数据的数据库表来为客户端执行某些权限工作。然后,在加载应用程序时,从这些表中加载用户数据,并且应用程序将决定他或她可以做什么。这实际上更容易,但可能存在风险。

    这意味着用户必须具有表级别的权限,而用户确实不应该拥有这些权限。由于数据库级别的权限更广泛,因此恶意用户可以更轻松地登录应用程序之外并执行他或她不应该能够执行的操作。如果您有可以轻松通过除用户应用程序之外的其他方式登录的内部用户(例如SQL Server的SSMS),则应特别注意此问题。

    如果您的数据库应用程序处于法律监管领域,例如金融或医疗保健领域,则需要非常严格地控制权限,并且不建议使用ORM而不是存储过程,因为您在任何时候都不应该在表级别上设置权限。


    1
    感谢您的时间@HLGEM,我不确定我是否表达清楚。您的回答让我担忧(并引起了负评)的原因是:“首先,用户不应该能够授予权限给别人,只有管理员才能这样做。如果您有良好的数据库设计,用户就绝不应该创建对象。”考虑像Google Drive这样的Web应用程序。 我可以在Google Drive中拥有一个文档或文件夹,并可以让其他人访问。同样地(更接近我提出的GitHub的例子),请考虑GitHub上的存储库,我可以决定哪些用户可以读/写该存储库。 - Michael
    我坚持我所说的。数据库不是文件系统,也不是存储库(非技术人员通常不使用),允许用户创建对象是危险的,他们不应该被允许这样做。用户不考虑数据安全性、数据库完整性和规范化等问题。除了数据库开发人员之外,任何人都不应该被允许在数据库中创建对象,这是不负责任的。用户没有必要的技术知识。 - HLGEM
    2
    @HLGEM请重新考虑这个问题。标题应该是“如何在关系数据库中建模(类似GitHub的)权限关系”,而不是“如何为关系数据库建模权限”。这不是关于设计用于访问通用数据库的权限模型,即DBMS的状态/元数据的一部分;而是关于为专门的数据库(“存储库”)设计权限模型,即应用程序管理用户组和组织的状态的一部分。操作应用程序接口可能是关系型的,就像DBMS一样,但也可能是面向权限的运算符。 - philipxy

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接