1.0 mvp
This commit is contained in:
@@ -111,25 +111,60 @@ fn build_authorization(config: &AppConfig, context: &RequestContext) -> Result<S
|
||||
.with_header("x-upstream-service", "convex")
|
||||
})?;
|
||||
|
||||
let dev_user_id = config.dev_user_id.trim();
|
||||
if dev_user_id.is_empty() {
|
||||
let Some(identity_user) = fallback_acting_identity_user(config, context) else {
|
||||
return Ok(format!("Convex {admin_key}"));
|
||||
}
|
||||
};
|
||||
|
||||
// 说明:Rust Web 直接调用 Convex HTTP API 时没有 Next/Convex Auth cookie。
|
||||
// 自托管开发态用 admin auth 携带 acting identity,让 @convex-dev/auth 的
|
||||
// getAuthUserId(ctx) 能得到 DEV_USER_ID,从而和 Next 开发态免登录语义一致。
|
||||
// 自托管开发态和 Hermes 委托 tool 调用都用 admin auth 携带 acting identity,
|
||||
// 让 @convex-dev/auth 的 getAuthUserId(ctx) 得到实际执行用户。
|
||||
let identity = json!({
|
||||
"subject": format!("{}|mnote-web-dev-session", dev_user_id),
|
||||
"issuer": "mnote-web-dev",
|
||||
"name": config.dev_user_name,
|
||||
"email": config.dev_user_email,
|
||||
"subject": format!("{}|{}", identity_user.user_id, identity_user.session_suffix),
|
||||
"issuer": identity_user.issuer,
|
||||
"name": identity_user.name,
|
||||
"email": identity_user.email,
|
||||
});
|
||||
let identity_encoded =
|
||||
base64::engine::general_purpose::STANDARD.encode(identity.to_string().as_bytes());
|
||||
Ok(format!("Convex {admin_key}:{identity_encoded}"))
|
||||
}
|
||||
|
||||
struct ActingIdentityUser {
|
||||
user_id: String,
|
||||
session_suffix: &'static str,
|
||||
issuer: &'static str,
|
||||
name: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
fn fallback_acting_identity_user(
|
||||
config: &AppConfig,
|
||||
context: &RequestContext,
|
||||
) -> Option<ActingIdentityUser> {
|
||||
let actor_id = context.auth.actor_id.trim();
|
||||
if !actor_id.is_empty() && actor_id != "anonymous" {
|
||||
return Some(ActingIdentityUser {
|
||||
user_id: actor_id.to_string(),
|
||||
session_suffix: "mnote-web-delegated-session",
|
||||
issuer: "mnote-web-delegated",
|
||||
name: actor_id.to_string(),
|
||||
email: String::new(),
|
||||
});
|
||||
}
|
||||
|
||||
let dev_user_id = config.dev_user_id.trim();
|
||||
if dev_user_id.is_empty() {
|
||||
return None;
|
||||
}
|
||||
Some(ActingIdentityUser {
|
||||
user_id: dev_user_id.to_string(),
|
||||
session_suffix: "mnote-web-dev-session",
|
||||
issuer: "mnote-web-dev",
|
||||
name: config.dev_user_name.clone(),
|
||||
email: config.dev_user_email.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_cookie_value(context: &RequestContext, name: &str) -> Option<String> {
|
||||
context
|
||||
.auth
|
||||
@@ -1175,6 +1210,31 @@ mod tests {
|
||||
assert_eq!(identity["email"], "dev@mnote.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_authorization_uses_delegated_actor_for_admin_identity() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("x-mnote-actor-id", HeaderValue::from_static("user_real_1"));
|
||||
headers.insert("x-mnote-actor-type", HeaderValue::from_static("user"));
|
||||
|
||||
let authorization =
|
||||
build_authorization(&config(), &request_context(headers)).expect("authorization");
|
||||
|
||||
assert!(authorization.starts_with("Convex admin-demo:"));
|
||||
let encoded = authorization
|
||||
.trim_start_matches("Convex admin-demo:")
|
||||
.trim();
|
||||
let decoded = base64::engine::general_purpose::STANDARD
|
||||
.decode(encoded)
|
||||
.expect("identity base64");
|
||||
let identity: serde_json::Value = serde_json::from_slice(&decoded).expect("identity json");
|
||||
assert_eq!(
|
||||
identity["subject"],
|
||||
"user_real_1|mnote-web-delegated-session"
|
||||
);
|
||||
assert_eq!(identity["issuer"], "mnote-web-delegated");
|
||||
assert_eq!(identity["name"], "user_real_1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_authorization_reads_convex_token_from_cookie() {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
Reference in New Issue
Block a user