返回更新列表
新发布Jul 14, 2026

kit-policy v2026.7.12

灵活、可组合的授权框架,适用于 Kit(灵感来自 Action Policy)

分享

kit-policy

一个灵活、可组合的授权框架,适用于Kit,灵感来自Ruby的Action Policy


[TOC]


文件

文件描述
.editorconfig编辑器格式配置
.gitignore构建产物和依赖的Git忽略规则
.tool-versionsasdf工具版本 (Zig, Kit)
LICENSEMIT许可证文件
README.md本文件
examples/blog-policy.kit博客授权示例
kit.toml包含元数据、任务和lint配置的包清单
src/core.kit核心授权辅助函数
src/error.kit授权错误、结果类型和失败原因
src/main.kit包根模块
src/scope.kit作用域、谓词和分页辅助函数
tests/policy.test.kit端到端策略行为测试
tests/types.test.kit策略类型和辅助函数测试

依赖

无Kit包依赖。

安装

kit add gitlab.com/kit-lang/packages/kit-policy.git

用法

import Kit.Policy.Core as PolicyCore
import Kit.Policy.Error as PolicyError
import Kit.Policy.Scope as PolicyScope

type Post = {id: Int, author-id: Int, published?: Bool, title: String}
type User = {id: Int, admin?: Bool}
type AuthContext = {user: User}

post-policy = fn(post, ctx, action) =>
  if ctx.user.admin? then
    PolicyCore.allow
  else
    match PolicyCore.resolve-alias action
      | :show -> PolicyCore.allow-if post.published?
      | :update -> PolicyCore.allow-if (ctx.user.id == post.author-id)
      | :destroy -> PolicyCore.allow-if (ctx.user.id == post.author-id)
      | _ -> PolicyCore.no-rule action

post-scope = fn(posts, ctx) =>
  if ctx.user.admin? then
    posts
  else
    posts |>> List.filter (fn(post) => post.published?)

main = fn =>
  user = {id: 1, admin?: false}
  ctx = {user: user}
  post = {id: 1, author-id: 1, published?: true, title: "Hello"}
  posts = [post]

  if PolicyCore.can-with? post-policy post ctx :update then
    println "Can update post"
  else
    println "Cannot update post"

  visible-posts = PolicyScope.scope-with post-scope posts ctx
  page = PolicyScope.paginate 1 10 visible-posts
  info = PolicyScope.pagination-info 1 10 visible-posts

  println "Visible posts: ${page}"
  println "Pages: ${info.pages}"

  err = PolicyError.not-authorized "Post" :update "not the author"
  println (PolicyError.message err)

main

API概览

Policy.Core

用于返回 Result Bool PolicyError 的策略函数的核心辅助函数。

PolicyCore.can-with? policy resource context action
PolicyCore.may-with? policy resource context action

PolicyCore.allow
PolicyCore.deny
PolicyCore.allow-if condition
PolicyCore.deny-if condition
PolicyCore.no-rule action
PolicyCore.allow-or-deny condition resource-name reason action

预检查辅助函数返回 Option BoolSome true 允许,Some false 拒绝,None 继续执行主规则。

PolicyCore.admin-bypass is-admin? ctx
PolicyCore.owner-check is-owner? resource ctx
PolicyCore.first-pre-check [check1, check2, check3]

动作辅助函数提供常见的动作组和别名。

PolicyCore.crud-actions
PolicyCore.read-actions
PolicyCore.write-actions

PolicyCore.read-action? action
PolicyCore.write-action? action

PolicyCore.resolve-alias :new     # :create
PolicyCore.resolve-alias :edit    # :update
PolicyCore.resolve-alias :delete  # :destroy
PolicyCore.resolve-alias :view    # :show

策略组合辅助函数将多个授权结果合并。

PolicyCore.all-allowed? [result1, result2, result3]
PolicyCore.any-allowed? [result1, result2, result3]

Policy.Scope

作用域辅助函数在将数据返回给调用者之前过滤集合。

PolicyScope.scope-with scope-fn items ctx
PolicyScope.filter-by predicate items

PolicyScope.is-owned-by? get-owner-id get-user-id item ctx
PolicyScope.is-published? get-published item
PolicyScope.is-in-state? get-state target-state item
PolicyScope.is-admin-or? check-admin fallback-check ctx item

谓词组合器对于构建可重用的作用域检查很有用。

PolicyScope.both? pred1 pred2 item
PolicyScope.either? pred1 pred2 item
PolicyScope.not-matching? pred item

分页辅助函数基于索引1。

page1 = PolicyScope.paginate 1 10 items
pages = PolicyScope.total-pages 10 items
info = PolicyScope.pagination-info 1 10 items

Policy.Error

用于授权失败的错误和结果类型。

type PolicyError =
  | NotAuthorized {resource: String, action: Keyword, reason: String}
  | RuleNotFound {action: Keyword}
  | ContextMissing {field: String}
  | PolicyNotFound {resource-type: String}
  | CustomError String

type FailureReason = FailureReason {
  policy: String,
  action: Keyword,
  details: String
}

type AuthResult =
  | Allowed
  | Denied String

辅助函数从模块导出,因此当导入为 PolicyError 时,可作为模块函数调用。

PolicyError.not-authorized resource action reason
PolicyError.rule-not-found action
PolicyError.context-missing field
PolicyError.policy-not-found resource-type
PolicyError.custom message

PolicyError.message err
PolicyError.kind err
PolicyError.is-not-authorized? err
PolicyError.is-rule-not-found? err

PolicyError.new policy action
PolicyError.with-details policy action details
PolicyError.policy reason
PolicyError.action reason
PolicyError.details reason
PolicyError.format reason

PolicyError.allowed
PolicyError.denied reason
PolicyError.is-allowed? result
PolicyError.is-denied? result
PolicyError.reason result
PolicyError.to-result resource-name action result

设计说明

  • 策略是普通函数,因此易于测试和组合。
  • 授权是显式的:辅助函数返回 Result Bool PolicyError 而不是抛出异常。
  • 作用域与策略检查分离,以便在渲染或序列化之前进行列表过滤。
  • 常见动作使用诸如 :index:show:create:update:destroy 等关键字。
  • 该包与框架无关,可用于任何Kit应用程序代码。

开发

运行示例

使用解释器运行博客策略示例:

kit run examples/blog-policy.kit

将示例编译为本地二进制文件:

kit build examples/blog-policy.kit && ./blog-policy

运行测试

运行测试套件:

kit test

带覆盖率运行测试套件:

kit test --coverage

运行 kit dev

运行标准开发工作流程(格式化、检查、测试):

kit dev

这将:

  1. 格式化并检查 src/ 中的源文件
  2. examples/ 中的示例进行类型检查
  3. tests/ 中运行带有覆盖率的测试

检查解释器/编译器一致性

运行示例的一致性检查:

kit parity --failures-only

生成文档

从文档注释生成API文档:

kit doc

注意:带有文档注释(##)的Kit源文件将在 docs/*.html 中生成HTML文档。

清理构建产物

移除生成的文件、缓存和构建产物:

kit task clean

注意:定义在 kit.toml 中。

本地安装

为开发目的在本地安装此包:

kit install

这会将包安装到 ~/.kit/packages/@kit/policy/,使其可供其他项目作为 Kit.Policy 导入。

许可证

此包根据MIT许可证发布 - 详情请参见 LICENSE

分类