// guard.mbt — G2 管线:四段 runner(verify→revocation→resolve→dispatch)
// D-G4/D-G13:段间纯值,Guard 单构造点收拢;hooks 全 async 型、核心零依赖
// (D-G12 案A);audit 不可失败(D-G11);业务错归 TResp,本面只管认证拒。
///|
/// 守护拒错(认证面)。
pub(all) enum GuardErr {
Verify(VerifyErr)
Revoked
Unresolved(String)
} derive(Eq, Debug)
///|
/// 审计事件:每段落一行;早期拒绝 sub/jti 可为空串(detail 带拒因)。
pub(all) struct AuditEvent {
stage : String
sub : String
jti : String
ok : Bool
detail : String
} derive(Eq, Debug)
///|
/// 会话认证协议位(D-G19:开放集,trait 非 enum)。框架私产的是校验序
/// 与短路逻辑;verify 的算法/协议在此换挡。缺省 TokenProtocol =
/// bearer token(初级无感);新协议 = 新 impl,管线零改。
/// 注:refresh 旋转属 token 协议的会话续命面,不在本 trait。
pub(open) trait AuthProtocol {
async fn verify_access(
self : Self,
config~ : GuardConfig,
signer~ : &Signer,
now~ : Int64,
token~ : String,
) -> Result[Claims, VerifyErr]
}
///|
/// bearer token 协议(缺省实现):HMAC 签名 access 票,核心 verify_for 形。
pub struct TokenProtocol {}
///|
pub fn TokenProtocol::new() -> TokenProtocol {
TokenProtocol::{ }
}
///|
pub impl AuthProtocol for TokenProtocol with fn verify_access(
self : TokenProtocol,
config~ : GuardConfig,
signer~ : &Signer,
now~ : Int64,
token~ : String,
) -> Result[Claims, VerifyErr] {
let _ = self
verify_for(config, signer, now, token, TokenType::Access)
}
///|
/// 单构造点(D-G13):三钩子由消费方注入;主体型 TSubj 泛型——框架
/// 不假设任何用户库(Resolver 消费方实现,茗篇 §2.6)。
pub(all) struct Guard[TSubj, TReq, TResp] {
config : GuardConfig
signer : &Signer
revocations : &RevocationStore
protocol : &AuthProtocol
resolve : async (String) -> TSubj?
dispatch : async (TSubj, TReq) -> TResp
/// 审计沉没槽:None = 吞流(D-G11 合法沉没)——缺席表示,不造空 async
/// 函数(空体字面量吃 [0067],deny-warn 门下即红)。
audit : (async (AuditEvent) -> Unit)?
}
///|
/// 初级方言(D-G17):全标签实参 + audit 可省——省略即吞流形 no-op
/// (D-G11 的合法沉没),初级三钩起步,进阶再挂 audit=。
/// protocol 可省——省略即缺省 bearer token(D-G19)。
pub fn[TSubj, TReq, TResp] Guard::new(
config~ : GuardConfig,
signer~ : &Signer,
revocations~ : &RevocationStore,
resolve~ : async (String) -> TSubj?,
dispatch~ : async (TSubj, TReq) -> TResp,
audit? : async (AuditEvent) -> Unit,
protocol? : &AuthProtocol,
) -> Guard[TSubj, TReq, TResp] {
let protocol : &AuthProtocol = match protocol {
Some(p) => p
None => TokenProtocol::new()
}
{ config, signer, revocations, protocol, resolve, dispatch, audit, }
}
///|
/// 审计发射单点:None = 吞流(D-G11 合法沉没),Some 直发。体内有 async
/// 调用,自身不触 [0067];管线发射不再构造空 async 函数。
async fn[TSubj, TReq, TResp] Guard::audit_emit(
self : Guard[TSubj, TReq, TResp],
event : AuditEvent,
) -> Unit {
match self.audit {
Some(emit) => emit(event)
None => ()
}
}
///|
/// 校验错误的审计短码(不携带载荷,防敏感物进审计)。
fn verify_detail(e : VerifyErr) -> String {
match e {
VerifyErr::Malformed(_) => "malformed"
VerifyErr::BadMac => "bad_mac"
VerifyErr::Expired(_) => "expired"
VerifyErr::NotYetValid(_) => "not_yet_valid"
VerifyErr::WrongIssuer(_) => "wrong_issuer"
VerifyErr::WrongAudience(_) => "wrong_audience"
VerifyErr::WrongType(_) => "wrong_type"
}
}
///|
/// access 门 runner:verify → 吊销核 → resolve → dispatch,段段审计
/// (fail 即短路,拒因落审计;成功路四行全 ok)。
pub async fn[TSubj, TReq, TResp] Guard::run(
self : Guard[TSubj, TReq, TResp],
now : Int64,
token : String,
req : TReq,
) -> Result[TResp, GuardErr] {
let claims = match
self.protocol.verify_access(
config=self.config,
signer=self.signer,
now~,
token~,
) {
Ok(c) => c
Err(e) => {
let _ = self.audit_emit({
stage: "verify",
sub: "",
jti: "",
ok: false,
detail: verify_detail(e),
})
return Err(GuardErr::Verify(e))
}
}
self.audit_emit({
stage: "verify",
sub: claims.sub,
jti: claims.jti,
ok: true,
detail: "",
})
if self.revocations.revoked(claims.jti) {
self.audit_emit({
stage: "revocation",
sub: claims.sub,
jti: claims.jti,
ok: false,
detail: "revoked",
})
return Err(GuardErr::Revoked)
}
self.audit_emit({
stage: "revocation",
sub: claims.sub,
jti: claims.jti,
ok: true,
detail: "",
})
let subject = match (self.resolve)(claims.sub) {
Some(s) => s
None => {
self.audit_emit({
stage: "resolve",
sub: claims.sub,
jti: claims.jti,
ok: false,
detail: "unresolved",
})
return Err(GuardErr::Unresolved(claims.sub))
}
}
self.audit_emit({
stage: "resolve",
sub: claims.sub,
jti: claims.jti,
ok: true,
detail: "",
})
let resp = (self.dispatch)(subject, req)
self.audit_emit({
stage: "dispatch",
sub: claims.sub,
jti: claims.jti,
ok: true,
detail: "",
})
Ok(resp)
}
///|
/// trait 方法显式晋升([0079] 标准动作)。
pub extend TokenProtocol with AuthProtocol::{verify_access}