///|
/// 认证域的类型定义。
///
/// 登录请求体与 core 的私有 `LoginRequest` 形状一致(`otp_code` 为 `None`
/// 时字段被整个省略,服务端只在字段非空时校验两步验证码),但这里要支持
/// `/api/auth/login/hash` 与 `/api/auth/login/ldap` 两条路径,所以单独定义。
///|
/// 登录请求体。
pub struct LoginBody {
/// 用户名。
username : String
/// 密码:`login` 传明文,`login_hash` 传静态哈希后的值,`login_ldap` 传明文。
password : String
/// 两步验证码;未开启 2FA 时留 `None`。
otp_code : String?
} derive(ToJson)
///|
/// 显式声明 `derive` 出来的 `ToJson` 实现以普通方法暴露。
pub extend VerifyTwoFactorRequest with ToJson::{to_json}
///|
/// 显式声明 `derive` 出来的 `ToJson` 实现以普通方法暴露(消除隐式提升告警)。
pub extend LoginBody with ToJson::{to_json}
///|
/// 两步验证码校验请求体(`POST /api/auth/2fa/verify`)。
pub struct VerifyTwoFactorRequest {
/// 验证器当前显示的 6 位码。
code : String
/// 生成 2FA 时返回的密钥。
secret : String
} derive(ToJson)
///|
/// 从登录响应的 `data` 里取出 token。
///
/// 缺少 `token`、token 不是字符串、或是空串都算协议错误:与其让调用方拿到
/// 一个空 token 之后再各处 401,不如在登录这一步就说清楚。
fn decode_token(data : Json) -> String raise @core.OpenListError {
guard data is Object(fields) else {
raise @core.OpenListError::Decode("登录响应不是 JSON 对象")
}
match fields.get("token") {
Some(String(value)) if value != "" => value
_ => raise @core.OpenListError::Decode("登录响应缺少 token")
}
}
///|
/// 两步验证的初始化结果(`POST /api/auth/2fa/generate`)。
pub struct TwoFactorAuth {
/// 二维码图片的 data URL(`data:image/png;base64,...`)。
qr : String
/// 密钥,用来在 `verify_2fa` 时回传。
secret : String
}
///|
/// 解码两步验证初始化结果。
pub impl @json.FromJson for TwoFactorAuth with fn from_json(json, path) {
guard json is Object(fields) else {
raise @json.JsonDecodeError((path, "TwoFactorAuth: expected object"))
}
{
qr: @core.string_field(fields, "qr"),
secret: @core.string_field(fields, "secret"),
}
}
///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend TwoFactorAuth with @json.FromJson::{from_json}