gpt4 book ai didi

haskell - 类型化分级访问控制系统

转载 作者:行者123 更新时间:2023-12-04 11:01:59 25 4
gpt4 key购买 nike

如何在 Haskell 中定义一个简单的分层访问控制系统?

我的角色是 Public > Contributor > Owner ,这些角色在层次结构中。 Public 能做到的一切也可以通过 Contributor 完成和 Owner ;等等。

同样,操作也在层次结构中:None > View > Edit .如果允许角色编辑,它也应该能够查看。

data Role = Public | Contributor | Owner
data Operation = None | View | Edit

newtype Policy = Policy (Role -> Operation)

在这个系统中,我可以将公共(public)可编辑策略表示为:

publicEditable :: Policy
publicEditable = Policy $ const Edit

但是类型系统并没有阻止我定义这样的愚蠢策略(允许 PublicEdit 但拒绝对 Owner 的任何访问):

stupidPolicy :: Policy
stupidPolicy = Policy check where
check Public = Edit
check Contributor = View
check Owner = None

如何在类型系统中表达角色和操作的层次结构?

最佳答案

任何有权访问 Policy 的人的构造函数可以采用 Policy分开并重新组合在一起,可能是一种 absurd 的方式。不要暴露 Policy此模块之外的构造函数。相反,请提供 smart constructor创建保证格式正确的策略并公开 Monoid接口(interface)来组合它们而不破坏不变量。保留Policy type abstract 确保所有可能导致无意义策略的代码都保存在此模块中。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Policy (
Role(..),
Level(..),
Policy, -- keep Policy abstract by not exposing the constructor
can
) where

import Data.Semigroup (Semigroup, Max(..))

data Role = Public | Contributor | Owner
deriving (Eq, Ord, Bounded, Enum, Show, Read)
data Level = None | View | Edit
deriving (Eq, Ord, Bounded, Enum, Show, Read)

下面我使用 GeneralizedNewtypeDeriving 借一双 Monoid来自 base 的实例: the monoid for functions ,它通过函数箭头逐点提升另一个幺半群,和 the Max newtype , 变成 Ord实例为 Monoid例如,始终选择 mappend 中的较大者的论据。

所以 PolicyMonoid实例将自动管理 Level 的排序制定策略时:在给定角色制定两个具有冲突级别的策略时,我们总是会选择更宽松的一个。这使得 <>附加操作:通过向“默认”策略 mempty 添加权限来定义策略,这是不授予任何人权限的一种。
newtype Policy = Policy (Role -> Max Level) deriving (Semigroup, Monoid)
grant是一个智能构造函数,它生成尊重 Role 的排序属性的策略和 Level .请注意,我将角色与 >= 进行比较确保向角色授予权限也会将该权限授予更多特权角色。
grant :: Role -> Level -> Policy
grant r l = Policy (Max . pol)
where pol r'
| r' >= r = l
| otherwise = None
can是一个观察结果,它告诉您策略是否将给定的访问级别授予给定的角色。我再次使用 >=以确保更宽松的水平意味着更宽松的水平。
can :: Role -> Level -> Policy -> Bool
(r `can` l) (Policy f) = getMax (f r) >= l

这个模块只占用了这么少的代码,我很惊喜!依靠 deriving机制,尤其是 GeneralizedNewtypeDeriving , 是让类型负责“无聊”代码的一种非常好的方式,这样您就可以专注于重要的事情。

这些策略的用法如下所示:
module Client where

import Data.Monoid ((<>))
import Policy

您可以使用 Monoid类从简单的策略中构建复杂的策略。
ownerEdit, contributorView, myPolicy :: Policy

ownerEdit = grant Owner Edit
contributorView = grant Contributor View
myPolicy = ownerEdit <> contributorView

您可以使用 can用于测试策略的功能。
canPublicView :: Policy -> Bool
canPublicView = Public `can` View

例如:
ghci> canPublicView myPolicy
False

关于haskell - 类型化分级访问控制系统,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41522159/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com