代码之家  ›  专栏  ›  技术社区  ›  Thom Smith

休息授权

  •  25
  • Thom Smith  · 技术社区  · 15 年前

    我正在为现实世界组织的成员构建一个基于社区的Rails站点。我正努力遵循宁静设计的最佳实践,而这其中的大部分或多或少都是书中所描述的。让我的大脑在一个干净、安静的圈子里运行的问题是授权。 认证 这是一个容易、长期解决的问题,有着广泛接受的RESTful解决方案,但是RESTful授权似乎有点像是一门黑色艺术。我正试图找到一种方法,它将提供最通用和最灵活的框架来控制对资源的访问,同时尽可能简单,同时符合一个宁静的体系结构。(还有一匹小马。)

    注意事项:

    1. 我需要控制对各种资源的访问,如用户、页面、帖子等。
    2. 给定资源的授权必须比简单CRUD更细粒度。
    3. 我希望允许自己和其他人从应用程序中编辑授权规则。
    4. 授权规则应该允许依赖于谓词,例如(概念上)所有者(用户、资源)或锁定(主题)

    考虑(2)是我最关心的。在我对权限的概念和对动作的宁静概念之间,似乎存在着一种阻抗不匹配。例如,发帖子(如在留言板中)。REST指示存在对post资源的四个操作:创建、读取、更新和删除。简单地说,用户应该能够更新自己的文章,但只允许某些用户(或角色或组)锁定它们。表示锁定的传统方法是在post的状态中,但这会导致在相同条件下的用户可能或可能无法根据他提供的(完全有效)值更新post的气味。在我看来,有两种不同的行为可以改变这个职位的状态,而强迫他们仅仅是为了掩饰对休息原则的违反。

    (我应该注意,这个问题与由于 无效或不一致 数据原则上,来自非特权用户的锁请求是相当有效的,只是不允许。)

    分解不是另一个词吗?

    这可以通过分解日志来克服:锁是特定日志的子资源,创建或销毁某个日志可能具有单独的权限。这一解有其休息环,但同时也带来了理论和实践上的困难。如果我考虑了锁,那么其他属性呢?假设我反复无常地决定,只允许管理员的一个成员修改文章的标题?然后,简单的授权更改将需要对数据库进行重组以适应它!这不是什么解决办法。为了在分解策略下允许这种灵活性,需要每个属性都是一个资源。这有点进退两难。我隐含的假设是,数据库中的资源被表示为一个表。在这种假设下,每个属性的资源意味着每个属性的表。显然,这是不实际的。然而,为了消除这种假设,表和资源之间会出现阻抗不匹配,这可能会打开自己的蠕虫罐头。使用这种方法需要比我给出的更深入的考虑。首先,用户合理地期望能够同时编辑多个属性。请求去哪里了?到包含所有属性的最小资源?平行于每个资源?去月球?

    有些事情和其他事情不一样

    那么假设我不分解属性。另一种选择似乎是为每个资源定义一组特权。然而,在这一点上,休息的同质性丧失了。为了定义资源的访问规则,系统必须对该资源的功能有特定的了解。此外,现在不可能一般地将权限传播给后代资源,即使子资源具有相同名称的特权,这些特权之间也没有固有的语义联系。在我看来,定义一个类似REST的标准权限集是两个领域中最糟糕的,因此对于每种类型的资源,我都被一个单独的权限层次结构所困扰。

    嗯,我们做了鼻子。还有帽子。但这是一种资源!

    我看到的一个建议是,将权限定义为创建/删除 资源 读/写 属性 . 这个系统在作为资源的属性和每个资源的特权之间进行了折衷:一个属性仍然只剩下CRUD,但是为了授权的目的,读和更新与属性有关,这可以被认为是伪资源。虽然概念完整性在某种程度上受到了损害,但这提供了作为资源方法的属性的许多实际好处。权限仍然可以从一个资源传播到另一个资源,从一个资源传播到另一个伪资源,但不能从一个伪资源传播。我还没有充分探讨这一战略的后果,但它似乎是有希望的。我突然想到这样一个系统最好作为模型的一个组成部分发挥作用。例如,在Rails中,它可能是 ActiveRecord . 这在我看来是相当激烈的,但授权是一个根本的交叉关注,这可能是合理的。

    哦,别忘了小马的事

    所有这些都忽略了可预测权限的问题。显然,用户应该能够编辑自己的文章,但不能编辑其他人的文章。同样明显的是,admin written permissions表不应该为每个用户都有单独的记录。这不是一个不寻常的要求,诀窍是让它通用。我认为我需要的所有功能都可以通过 规则 具有预测性,以便快速、迅速地确定规则的适用性。规则“ allow User write Post where Author(User, Post) “将转换为” for all User, Post such that Author(User, Post), allow User write Post “和” deny all write Post where Locked(Post) “to” for all Post such that Locked(Post), deny all write Post “。(将会是 宏伟的 如果所有这些谓词都可以用一个用户和一个资源来表示),则概念上的结果“最终”规则将是不可预测的。这就提出了如何实现这样一个系统的问题。谓词应该是模型类的成员,但我不确定如何在规则的上下文中优雅地引用它们。要安全地做到这一点,需要进行某种反思。在这里,我再次感觉到这将需要对模型实现进行改造。

    你怎么再拼一次?

    最后一个问题是如何最好地将这些授权规则表示为数据。数据库表可能会做到这一点,它的枚举列用于allow/deny和c/r/u/d(或者c r u d位)?或者C、R、U、D允许、拒绝、继承?),以及带有路径的资源列。也许,为了方便起见,一个“继承”位。就谓词而言,我有点茫然。分开的桌子?当然有很多缓存可以防止 不虔诚的慢。


    我想这是很需要的。我试着在问问题之前做作业,但此时我真的需要一个外部的视角。我很感激你们对这个问题的任何意见。

    3 回复  |  直到 9 年前
        1
  •  8
  •   Darrel Miller    15 年前

    对不起,我没有时间公正地回答这个问题,但很高兴看到一些经过深思熟虑的问题。 以下是一些评论:

    不要陷入将HTTP动词映射到CRUD的陷阱。是的,非常干净地获取和删除映射,但是put可以创建和更新(但仅限于完全替换),post是一个通配符动词。post实际上是处理所有不适合GET、PUT和 删除。

    使用属性来表示对象的状态只是状态管理的一种方法。我想您可以想象一下以下请求可能会做什么:

    POST /LockedPosts?url=/Post/2010
    

    子资源也是管理资源当前状态的有效方法。我觉得没有必要以一致的方式处理资源的“状态”属性及其“数据”属性。

    试图将资源直接映射到表将严重限制您。别忘了,当你遵循其余的约束条件时,你会突然发现你可用的动词非常有限。你需要能够在你使用的资源中保持创造性来弥补这一点。将自己限制在一个资源等于一个表将严重限制最终应用程序的功能。

    我们经常看到Rails、ASP.NET MVC和WCF REST用户在StackOverflow上发布关于在REST约束下如何执行某些操作的问题。问题通常不是REST的约束,而是框架在支持RESTful应用程序方面的限制。我认为首先找到一个问题的宁静解决方案,然后看看它是否可以映射回您选择的框架是至关重要的。

    就创建一个比资源本身更精细的权限模型而言。记住,REST的一个关键约束是超媒体。超媒体不仅可以用于查找相关实体,还可以用于表示有效/允许的状态转换。如果返回的表示形式不包含嵌入链接,则可以根据权限有条件地控制谁可以执行哪些操作。例如,如果用户有权解锁Post 342,则可以返回嵌入在表示中的以下链接:

    <Link href="/UnlockedPosts?url=/Post/342" method="POST"/>
    

    如果他们没有该权限,则不要返回链接。

    我认为你在这里的一个困难是你试图一下子把一个大问题考虑得太多。我认为您需要将尝试向客户机公开的RESTful接口视为与管理权限和谓词以管理域模型中的授权不同的问题。

    我知道我没有直接回答你的任何问题,但希望我提供了一些观点,可能在某种程度上有所帮助。

        2
  •  2
  •   Thom Smith    13 年前

    我最近发现了一个认证解决方案,似乎可以解决我的大部分问题。如果你喜欢这个问题,你可能会感兴趣:

    https://github.com/stffn/declarative_authorization

        3
  •  0
  •   Mike    15 年前

    正如达雷尔所指出的,休息不是积垢。如果您发现所标识的资源的粒度太粗,统一接口无法提供足够的控制,那么请将资源拆分为子资源,并将原始资源用作指向其组件的超链接的“集合”。