Skip to main content

gatekeep_keepsake/
subject.rs

1use gatekeep::Context;
2use keepsake::{KeepsakeError, SubjectRef};
3
4/// Maps a gatekeep request context to the keepsake subject being resolved.
5pub trait SubjectMapper: Send + Sync {
6    /// Builds the keepsake subject for this request.
7    ///
8    /// # Errors
9    ///
10    /// Returns [`KeepsakeError`] when the mapped subject is invalid.
11    fn subject(&self, cx: &Context) -> Result<SubjectRef, KeepsakeError>;
12}
13
14/// Default tenant-aware subject mapper.
15///
16/// It keeps the principal id unchanged and prefixes the principal kind with the
17/// tenant id, so equal principal ids in different tenants do not share keepsake
18/// relations. Applications with an existing subject convention can provide a
19/// custom [`SubjectMapper`] instead.
20#[derive(Clone, Copy, Debug, Default)]
21pub struct TenantScopedSubjectMapper;
22
23impl SubjectMapper for TenantScopedSubjectMapper {
24    fn subject(&self, cx: &Context) -> Result<SubjectRef, KeepsakeError> {
25        SubjectRef::new(
26            tenant_principal_kind(cx.tenant.as_str(), cx.principal.kind()),
27            cx.principal.id(),
28        )
29    }
30}
31
32/// Principal-only subject mapper for applications that already encode tenancy
33/// in keepsake subject identifiers.
34#[derive(Clone, Copy, Debug, Default)]
35pub struct PrincipalSubjectMapper;
36
37impl SubjectMapper for PrincipalSubjectMapper {
38    fn subject(&self, cx: &Context) -> Result<SubjectRef, KeepsakeError> {
39        SubjectRef::new(cx.principal.kind(), cx.principal.id())
40    }
41}
42
43/// Maps a gatekeep context through [`TenantScopedSubjectMapper`].
44///
45/// # Errors
46///
47/// Returns [`KeepsakeError`] if the mapped subject is invalid.
48pub fn tenant_scoped_subject(cx: &Context) -> Result<SubjectRef, KeepsakeError> {
49    TenantScopedSubjectMapper.subject(cx)
50}
51
52/// Maps a gatekeep context through [`PrincipalSubjectMapper`].
53///
54/// # Errors
55///
56/// Returns [`KeepsakeError`] if the mapped subject is invalid.
57pub fn principal_subject(cx: &Context) -> Result<SubjectRef, KeepsakeError> {
58    PrincipalSubjectMapper.subject(cx)
59}
60
61fn tenant_principal_kind(tenant: &str, principal_kind: &str) -> String {
62    format!(
63        "tenant:{}:{}principal:{}:{}",
64        tenant.len(),
65        tenant,
66        principal_kind.len(),
67        principal_kind
68    )
69}