如何仅通过 _security 对象把数据库的读写权限授予“reader”与“writer”两个角色?

解读

国内面试中,这道题考察的是“能否在不借助额外设计文档、不改动默认安全处理器的前提下,仅用 _security 对象完成最小权限闭环”。
很多候选人会下意识回答“在 validate_doc_update 里做判断”或“再加一个设计文档”,这都会被判为跑题。
正确姿势是:把 _security 当成数据库级 ACL,一次性声明“谁可以读、谁可以写”,不引入任何其他逻辑
面试官常追问:

  1. 如果同时出现 names 与 roles,优先级如何?
  2. 匿名用户是否还能写?
  3. 该配置在集群里多久可见?
    回答必须围绕 _security 的原子性、即时性、排他性展开。

知识点

  • _security 是 CouchDB 的数据库级安全文档,路径为 /{db}/_security不在 _users 系统表里
  • 合法顶层字段只有两个:adminsmembers,各自包含 names 数组与 roles 数组。
  • members.roles 决定“读+写”权限;admins.roles 决定“读+写+删+改安全文档”的超级权限。
  • 若未在 _security 中显式声明,默认所有人拥有读写权限(国内公有云通常已关闭该默认)。
  • 修改 _security 不需要重启节点,在集群中通过内部 RPC 秒级同步。
  • 验证顺序:先匹配 admins(任意命中立即放行),再匹配 members;都不命中则拒绝

答案

向目标数据库发送一次 PUT 请求,只保留 members 字段,显式写入角色列表即可:

PUT /{db}/_security
Content-Type: application/json

{
  "members": {
    "roles": ["reader","writer"]
  }
}

返回 200 即生效。
解释要点:

  1. 未声明 admins,因此管理员只能由 server admin(.ini 里 [admins] 段落)充当,防止普通用户提权。
  2. 未写 names 数组,避免硬编码账号,符合“角色而非个人”的国内审计规范。
  3. 此时:
    • 拥有 reader 或 writer 角色的用户可读取所有文档;
    • 拥有 reader 或 writer 角色的用户可写入所有文档;
    • 匿名用户或没有这两个角色的账号任何操作都会收到 401
  4. 若后续需要回收权限,只需从 roles 数组中移除对应角色并再次 PUT,无需改代码、无需重建索引

拓展思考

  1. 国内等保 2.0 要求“三权分立”,可把角色拆细:
    reader → 只读审计角色;writer → 业务写角色;再建一个 dba 角色放入 admins.roles,实现“读写删”与“改安全”分离。
  2. 若使用 CouchDB 3.x+,可配合 _partitioned 数据库与 db-per-user 模式,把 _security 的 roles 与 JWT 中的 roles claim 映射,实现微服务场景下的多租户隔离,而无需写一行 validate 函数。
  3. 注意 roles 是数组完全匹配,不支持通配符;如果业务线很多,建议角色命名加前缀,如 app1_readerapp2_writer,避免跨业务越权。
  4. 面试加分点:提到写 _security 需要 server admin 身份,普通 db member 无法篡改;同时建议打开 [couch_httpd_auth] require_valid_user = true,关闭匿名访问,满足国内公有云合规扫描