298 lines
11 KiB
Rust
298 lines
11 KiB
Rust
// JWT 解析在计时前完成;本场景测 Provider 数据库复核及随后实际角色读取。
|
|
use dever_runtime::api::{Request, Response};
|
|
|
|
struct AuthInput {
|
|
service: Arc<auth::Service>,
|
|
requests: Vec<Request>,
|
|
claims: Vec<Claims>,
|
|
now: i64,
|
|
}
|
|
|
|
fn prepare(path: &Path) -> (Arc<dever_runtime::api::Config>, AuthInput) {
|
|
let config = Arc::new(dever_runtime::api::Config::load(path, &["admin"]).unwrap());
|
|
let service = Arc::new(
|
|
auth::Service::new(
|
|
config.clone(),
|
|
&[auth::Verification {
|
|
name: "bench.account.verify",
|
|
user_model: AUTH_USER_MODEL,
|
|
tenant_model: AUTH_TENANT_MODEL,
|
|
}],
|
|
&[auth::Route {
|
|
topic: "admin",
|
|
required: true,
|
|
user_models: &[AUTH_USER_MODEL],
|
|
tenant_models: &[AUTH_TENANT_MODEL],
|
|
}],
|
|
)
|
|
.unwrap(),
|
|
);
|
|
let now = auth::now().unwrap();
|
|
let claims = (0..17)
|
|
.map(|index| Claims {
|
|
subject: format!("account-{index:02} 中文\""),
|
|
session: format!("session-{index}"),
|
|
site: "admin".into(),
|
|
tenant: None,
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let requests = claims
|
|
.iter()
|
|
.map(|claims| {
|
|
// 只用公开 Cookie 接口准备凭据,不读取 Secret 的私有字节。
|
|
let mut bootstrap = Request::new(Default::default());
|
|
*bootstrap.method_mut() = "POST".parse().unwrap();
|
|
*bootstrap.uri_mut() = "/login".parse().unwrap();
|
|
bootstrap
|
|
.headers_mut()
|
|
.insert("host", "benchmark.test".parse().unwrap());
|
|
bootstrap
|
|
.headers_mut()
|
|
.insert("origin", "https://benchmark.test".parse().unwrap());
|
|
let (mut context, _) = service.prepare("admin", &bootstrap, true, now).unwrap();
|
|
context
|
|
.issue_cookie(&claims.subject, &claims.session, None, now)
|
|
.unwrap();
|
|
let mut response = Response::new(Default::default());
|
|
context.publish(&mut response);
|
|
let cookie = response.headers().get("set-cookie").unwrap();
|
|
let cookie = std::str::from_utf8(cookie.as_bytes())
|
|
.unwrap()
|
|
.split(';')
|
|
.next()
|
|
.unwrap()
|
|
.as_bytes()
|
|
.to_vec();
|
|
let mut request = Request::new(Default::default());
|
|
*request.uri_mut() = "/bench/account/admin/permissions".parse().unwrap();
|
|
request
|
|
.headers_mut()
|
|
.insert("host", "benchmark.test".parse().unwrap());
|
|
request
|
|
.headers_mut()
|
|
.insert("cookie", cookie.try_into().unwrap());
|
|
request
|
|
})
|
|
.collect();
|
|
(
|
|
config,
|
|
AuthInput {
|
|
service,
|
|
requests,
|
|
claims,
|
|
now,
|
|
},
|
|
)
|
|
}
|
|
|
|
fn measure_auth(
|
|
runtime: &Runtime,
|
|
registry: &Arc<Registry>,
|
|
input: &AuthInput,
|
|
generated: bool,
|
|
roles: bool,
|
|
count: usize,
|
|
) -> (u128, u64) {
|
|
runtime
|
|
.run(async |scope| {
|
|
let mut contexts = input.requests[..16]
|
|
.iter()
|
|
.map(|request| {
|
|
input
|
|
.service
|
|
.prepare("admin", request, false, input.now)
|
|
.unwrap()
|
|
.0
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let mut checksum = 0u64;
|
|
let start = Instant::now();
|
|
for index in 0..count {
|
|
let position = index % contexts.len();
|
|
let context = &mut contexts[position];
|
|
verify(
|
|
generated,
|
|
context,
|
|
black_box(&input.claims[position]).clone(),
|
|
scope,
|
|
registry,
|
|
)
|
|
.await
|
|
.map_err(|error| error.0)
|
|
.unwrap();
|
|
checksum += black_box(
|
|
context.user_id(None).unwrap().unwrap() as u64
|
|
+ context.id().unwrap().len() as u64,
|
|
);
|
|
if roles {
|
|
let mut database = DbContext::new(registry.clone());
|
|
let values =
|
|
black_box(permissions(generated, context, scope, &mut database).await);
|
|
for value in &values {
|
|
for field in value {
|
|
checksum += field.bytes().map(u64::from).sum::<u64>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
(start.elapsed().as_nanos(), checksum)
|
|
})
|
|
.unwrap()
|
|
}
|
|
|
|
fn check_auth(runtime: &Runtime, registry: &Arc<Registry>, input: &AuthInput) {
|
|
runtime
|
|
.run(async |scope| {
|
|
let expected = vec![
|
|
[
|
|
"bench.account.admin.permissions",
|
|
"bench",
|
|
"account",
|
|
"admin",
|
|
"permissions",
|
|
"GET",
|
|
"http",
|
|
]
|
|
.map(String::from),
|
|
];
|
|
for generated in [true, false] {
|
|
for index in 0..16 {
|
|
let (mut context, claims) = input
|
|
.service
|
|
.prepare("admin", &input.requests[index], false, input.now)
|
|
.unwrap();
|
|
verify(generated, &mut context, claims.unwrap(), scope, registry)
|
|
.await
|
|
.map_err(|error| error.0)
|
|
.unwrap();
|
|
assert_eq!(context.id().unwrap(), input.claims[index].subject);
|
|
assert_eq!(
|
|
context.user_id(Some(AUTH_USER_MODEL)).unwrap(),
|
|
Some(index as i64 + 1)
|
|
);
|
|
assert!(
|
|
context
|
|
.tenant_id(Some(AUTH_TENANT_MODEL))
|
|
.unwrap()
|
|
.is_none()
|
|
);
|
|
let mut database = DbContext::new(registry.clone());
|
|
assert_eq!(
|
|
permissions(generated, &mut context, scope, &mut database).await,
|
|
expected
|
|
);
|
|
}
|
|
for (index, wrong_session) in [(16, false), (0, true)] {
|
|
let (mut context, claims) = input
|
|
.service
|
|
.prepare("admin", &input.requests[index], false, input.now)
|
|
.unwrap();
|
|
let mut claims = claims.unwrap();
|
|
if wrong_session {
|
|
claims.session.push_str("-revoked");
|
|
}
|
|
assert_eq!(
|
|
verify(generated, &mut context, claims, scope, registry)
|
|
.await
|
|
.unwrap_err()
|
|
.0,
|
|
401
|
|
);
|
|
assert!(context.id().is_err());
|
|
}
|
|
}
|
|
})
|
|
.unwrap();
|
|
}
|
|
|
|
fn main() {
|
|
let (directory, path) = directory(
|
|
"auth",
|
|
r#""http":{"host":"127.0.0.1","port":18001},"sites":{"admin":{"path":"admin","hosts":["benchmark.test"],"auth":"session","origin":"https://benchmark.test"}},"auth":{"providers":{"session":{"verify":"bench.account.verify","jwtSecret":"benchmark-only-key-not-for-any-deployment","ttlSeconds":3600,"cookie":"session"}}}"#,
|
|
);
|
|
let runtime = runtime();
|
|
let registry = runtime
|
|
.run(async |scope| database_registry(&path, scope).await)
|
|
.unwrap()
|
|
.unwrap();
|
|
let (config, input) = prepare(&path);
|
|
runtime
|
|
.run(async |scope| {
|
|
let mut database = DbContext::new(registry.clone());
|
|
for (index, claims) in input.claims.iter().enumerate() {
|
|
let account = GENERATED_SEED(
|
|
&claims.subject,
|
|
&claims.session,
|
|
index < 16,
|
|
scope,
|
|
&mut database,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
assert_eq!(account.field0.0, index as i64 + 1);
|
|
}
|
|
let (mut context, claims) = input
|
|
.service
|
|
.prepare("admin", &input.requests[0], false, input.now)
|
|
.unwrap();
|
|
drop(database);
|
|
api_verify(&mut context, claims.unwrap(), scope, ®istry)
|
|
.await
|
|
.map_err(|error| error.0)
|
|
.unwrap();
|
|
let mut database = DbContext::new(registry.clone());
|
|
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
|
|
dever_runtime::authorization::synchronize(
|
|
executor,
|
|
scope,
|
|
&config,
|
|
&[dever_runtime::authorization::Declaration {
|
|
topic: "admin",
|
|
component: "bench",
|
|
domain: "account",
|
|
action: "permissions",
|
|
method: "GET",
|
|
}],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
dever_runtime::authorization::save(
|
|
&mut database,
|
|
AUTH_BINDING,
|
|
scope,
|
|
&context,
|
|
"reader",
|
|
"Benchmark reader",
|
|
false,
|
|
&["bench.account.admin.permissions".into()],
|
|
)
|
|
.await
|
|
.unwrap();
|
|
for user in 1..=16 {
|
|
let executor = database.on(AUTH_BINDING, scope).await.unwrap();
|
|
dever_runtime::authorization::grant(executor, scope, &context, user, "reader")
|
|
.await
|
|
.unwrap();
|
|
}
|
|
})
|
|
.unwrap();
|
|
check_auth(&runtime, ®istry, &input);
|
|
header();
|
|
if HTTP_BENCH {
|
|
protected_http::run(&runtime, ®istry, &config, &input);
|
|
} else {
|
|
for (name, roles) in [
|
|
("auth_provider_verify", false),
|
|
("auth_provider_role_read", true),
|
|
] {
|
|
samples(name, |generated, count| {
|
|
measure_auth(&runtime, ®istry, &input, generated, roles, count)
|
|
});
|
|
}
|
|
}
|
|
check_auth(&runtime, ®istry, &input);
|
|
drop(registry);
|
|
drop(runtime);
|
|
drop(directory);
|
|
}
|