steid

@jamesgill /

1.0 KBCode·Blame·Raw
1//! Authorization predicates shared between use cases.
2//!
3//! One answer per question, in one place. A predicate copied into a second use case
4//! is a predicate that will eventually disagree with itself — and an authorization
5//! check that disagrees with itself fails open somewhere.
6
7use crate::domain::{Actor, Organization, Role, repository::MembershipRepository};
8
9use super::error::Result;
10
11/// Whether the actor owns this organisation.
12///
13/// Owner is strictly stronger than membership: a member may read what they can see,
14/// but editing the profile, creating a repository, and pushing are all owner-only.
15/// "Signed in" quietly becoming "allowed" is the usual way this goes wrong.
16pub(crate) async fn is_org_owner(
17 org: &Organization,
18 actor: &Actor,
19 memberships: &impl MembershipRepository,
20) -> Result<bool> {
21 let Some(user_id) = actor.user_id() else {
22 return Ok(false);
23 };
24
25 Ok(memberships
26 .find(&org.id, user_id)
27 .await?
28 .is_some_and(|membership| membership.role == Role::Owner))
29}