mirror of
https://github.com/dani-garcia/vaultwarden.wiki.git
synced 2026-11-14 17:46:07 +02:00
Check membership for the organization public key in the handler
The public key routes serve the placeholder SSO organization's key to users without an organization, so they couldn't require the member guard, but it still ran for every request: it logged a membership error for each legitimate v2 JIT password setup and authenticated the request twice. The routes now authenticate once, return the placeholder key first, and check the membership themselves, with the same rule as the guard.
This commit is contained in:
1 file changed
+12
-20
@@ -3191,12 +3191,7 @@ struct OrganizationUserRecoverAccountRequest {
|
|||||||
// But the clients do not seem to use this at all
|
// But the clients do not seem to use this at all
|
||||||
// Just add it here in case they will
|
// Just add it here in case they will
|
||||||
#[get("/organizations/<org_id>/public-key")]
|
#[get("/organizations/<org_id>/public-key")]
|
||||||
async fn get_organization_public_key(
|
async fn get_organization_public_key(org_id: OrganizationId, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
org_id: OrganizationId,
|
|
||||||
headers: Headers,
|
|
||||||
member: Result<OrgMemberHeaders, &'static str>,
|
|
||||||
conn: DbConn,
|
|
||||||
) -> JsonResult {
|
|
||||||
// SSO users without an org get the fake one, whose key the v2 JIT password flow fetches anyway
|
// SSO users without an org get the fake one, whose key the v2 JIT password flow fetches anyway
|
||||||
if org_id.eq_ignore_ascii_case(FAKE_SSO_IDENTIFIER) && headers.user.private_key.is_none() {
|
if org_id.eq_ignore_ascii_case(FAKE_SSO_IDENTIFIER) && headers.user.private_key.is_none() {
|
||||||
return Ok(Json(json!({
|
return Ok(Json(json!({
|
||||||
@@ -3204,14 +3199,16 @@ async fn get_organization_public_key(
|
|||||||
"publicKey": fake_sso_org_public_key().await?,
|
"publicKey": fake_sso_org_public_key().await?,
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
let member = match member {
|
|
||||||
Ok(member) => member,
|
|
||||||
// The guard already logged it
|
|
||||||
Err(e) => return Err(crate::error::Error::new_msg(e).with_code(Status::Unauthorized.code)),
|
|
||||||
};
|
|
||||||
|
|
||||||
if org_id != member.membership.org_uuid {
|
// Only for members, as `OrgMemberHeaders` checks it, but here, so that the fake org above neither logs a
|
||||||
err!("Organization not found", "Organization id's do not match");
|
// membership error nor authenticates the request twice: a known role, and not revoked (invited members are,
|
||||||
|
// while joining)
|
||||||
|
let is_member = Membership::find_by_user_and_org(&headers.user.uuid, &org_id, &conn).await.is_some_and(|member| {
|
||||||
|
MembershipType::from_i32(member.atype).is_some()
|
||||||
|
&& MembershipStatus::from_i32(member.status).is_some_and(|status| status != MembershipStatus::Revoked)
|
||||||
|
});
|
||||||
|
if !is_member {
|
||||||
|
err_code!("You need to be a Member of the Organization to call this endpoint", Status::Unauthorized.code)
|
||||||
}
|
}
|
||||||
let Some(org) = Organization::find_by_uuid(&org_id, &conn).await else {
|
let Some(org) = Organization::find_by_uuid(&org_id, &conn).await else {
|
||||||
err!("Organization not found")
|
err!("Organization not found")
|
||||||
@@ -3240,13 +3237,8 @@ async fn fake_sso_org_public_key() -> ApiResult<String> {
|
|||||||
// Obsolete - Renamed to public-key (2023.8), left for backwards compatibility with older clients
|
// Obsolete - Renamed to public-key (2023.8), left for backwards compatibility with older clients
|
||||||
// https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Api/AdminConsole/Controllers/OrganizationsController.cs#L487-L492
|
// https://github.com/bitwarden/server/blob/9ebe16587175b1c0e9208f84397bb75d0d595510/src/Api/AdminConsole/Controllers/OrganizationsController.cs#L487-L492
|
||||||
#[get("/organizations/<org_id>/keys")]
|
#[get("/organizations/<org_id>/keys")]
|
||||||
async fn get_organization_keys(
|
async fn get_organization_keys(org_id: OrganizationId, headers: Headers, conn: DbConn) -> JsonResult {
|
||||||
org_id: OrganizationId,
|
get_organization_public_key(org_id, headers, conn).await
|
||||||
headers: Headers,
|
|
||||||
member: Result<OrgMemberHeaders, &'static str>,
|
|
||||||
conn: DbConn,
|
|
||||||
) -> JsonResult {
|
|
||||||
get_organization_public_key(org_id, headers, member, conn).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will allow to reset 2FA too
|
// Will allow to reset 2FA too
|
||||||
|
|||||||
Reference in new issue
Block a user