Skip to content

Commit

Permalink
Impl PrivateClaims for AnyClaims.
Browse files Browse the repository at this point in the history
Add `NoClaim` empty type.
Add documentation on `Claim` trait.
  • Loading branch information
timothee-haudebourg committed May 13, 2024
1 parent 15b5fac commit 56fb310
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 30 deletions.
4 changes: 2 additions & 2 deletions crates/claims/crates/jwt/src/claims/mixed/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use serde::{de::DeserializeOwned, Serialize};
use ssi_claims_core::{ClaimsValidity, DateTimeEnvironment, Validate};
use ssi_jws::JWSPayload;

use crate::{GetClaim, RemoveClaim, SetClaim, TryRemoveClaim, TrySetClaim};
use crate::{GetClaim, PrivateClaimSet, RemoveClaim, SetClaim, TryRemoveClaim, TrySetClaim};

use super::{Claim, ClaimSet, RegisteredClaims, TryGetClaim};

Expand Down Expand Up @@ -202,7 +202,7 @@ impl IntoIterator for AnyClaims {
}
}

impl ClaimSet for AnyClaims {
impl PrivateClaimSet for AnyClaims {
type Error = serde_json::Error;
}

Expand Down
57 changes: 53 additions & 4 deletions crates/claims/crates/jwt/src/claims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,46 @@ use ssi_claims_core::{ClaimsValidity, DateTimeEnvironment, InvalidClaims};
///
/// You do not need to implement this trait yourself to define a private claim
/// type. Simply implement the [`PrivateClaim`] trait instead.
///
/// # Why is this trait so complicated?
///
/// JWT makes the distinction between registered claims defined in the
/// [IANA registry][1] and private (application specific) claims. To manage them
/// easily, this library provides the [`JWTClaims`] type that combines public
/// claims and private claims. This type implements the generic [`ClaimSet`]
/// trait, allowing one to read (or write) any claim, registered or not, using
/// [`ClaimSet::get`] (or [`ClaimSet::set`]).
///
/// For this method to work however, we must have a mechanism to recognize if
/// the claim, provided as type parameter, is a registered claim or a private
/// claim.
/// This will decide from which underlying set the claim will be pulled
/// ([`JWTClaims::registered`] or [`JWTClaims::private`]).
///
/// Currently this cannot be done statically because Rust has no support for
/// [specialization][2] yet. Instead we use associated consts/types and methods
/// to specialize claims dynamically (although the Rust compiler should be
/// smart enough to optimize away all of the added complexity).
///
/// [1]: <https://www.iana.org/assignments/jwt/jwt.xhtml>
/// [2]: <https://github.com/rust-lang/rust/issues/31844>
pub trait Claim: Clone {
/// This same claim type, as a private claim.
type Private: PrivateClaim;

/// This same claim type, as a registered claim.
/// Registered claim type specialization.
///
/// This must be `Self` if this is a registered claim, and [`NoClaim`] if
/// it is a private claim.
type Registered: RegisteredClaim;

/// Private claim type specialization.
///
/// This must be `Self` if this is a private claim, and [`NoClaim`] if
/// it is a registered claim.
type Private: PrivateClaim;

/// Claim name, used as key in the JSON representation.
const JWT_CLAIM_NAME: &'static str;

/// Is this a registered claim type?
const IS_REGISTERED_JWT_CLAIM: bool;

fn from_registered(claim: Self::Registered) -> Option<Self>;
Expand Down Expand Up @@ -53,6 +85,23 @@ pub trait Claim: Clone {
fn into_registered(self) -> Result<Self::Registered, Self::Private>;
}

/// Empty claim.
///
/// This is the empty claim type used for registered/private claim
/// specialization.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum NoClaim {}

impl fmt::Display for NoClaim {
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
unreachable!()
}
}

impl PrivateClaim for NoClaim {
const JWT_PRIVATE_CLAIM_NAME: &'static str = "";
}

/// Set of JWT claims.
pub trait ClaimSet {
type Error: fmt::Display;
Expand Down
32 changes: 14 additions & 18 deletions crates/claims/crates/jwt/src/claims/private.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{RemoveClaim, TryGetClaim, TryRemoveClaim, TrySetClaim};
use crate::{NoClaim, RemoveClaim, TryGetClaim, TryRemoveClaim, TrySetClaim};

use super::{Claim, ClaimSet, GetClaim, SetClaim};
use core::fmt;
Expand All @@ -10,7 +10,7 @@ pub trait PrivateClaim: Clone {

impl<T: PrivateClaim> Claim for T {
type Private = Self;
type Registered = std::convert::Infallible;
type Registered = NoClaim;

const JWT_CLAIM_NAME: &'static str = T::JWT_PRIVATE_CLAIM_NAME;
const IS_REGISTERED_JWT_CLAIM: bool = false;
Expand Down Expand Up @@ -44,10 +44,6 @@ impl<T: PrivateClaim> Claim for T {
}
}

impl PrivateClaim for std::convert::Infallible {
const JWT_PRIVATE_CLAIM_NAME: &'static str = "";
}

pub trait PrivateClaimSet {
type Error: fmt::Display;
}
Expand All @@ -56,38 +52,38 @@ impl<T: PrivateClaimSet> ClaimSet for T {
type Error = <T as PrivateClaimSet>::Error;
}

impl<T: PrivateClaimSet> GetClaim<std::convert::Infallible> for T {
fn get_claim(&self) -> Option<Cow<std::convert::Infallible>> {
impl<T: PrivateClaimSet> GetClaim<NoClaim> for T {
fn get_claim(&self) -> Option<Cow<NoClaim>> {
None
}
}

impl<T: PrivateClaimSet> TryGetClaim<std::convert::Infallible> for T {
fn try_get_claim(&self) -> Result<Option<Cow<std::convert::Infallible>>, Self::Error> {
impl<T: PrivateClaimSet> TryGetClaim<NoClaim> for T {
fn try_get_claim(&self) -> Result<Option<Cow<NoClaim>>, Self::Error> {
Ok(None)
}
}

impl<T: PrivateClaimSet> SetClaim<std::convert::Infallible> for T {
fn set_claim(&mut self, _: std::convert::Infallible) {
impl<T: PrivateClaimSet> SetClaim<NoClaim> for T {
fn set_claim(&mut self, _: NoClaim) {
unreachable!()
}
}

impl<T: PrivateClaimSet> TrySetClaim<std::convert::Infallible> for T {
fn try_set_claim(&mut self, _: std::convert::Infallible) -> Result<(), Self::Error> {
impl<T: PrivateClaimSet> TrySetClaim<NoClaim> for T {
fn try_set_claim(&mut self, _: NoClaim) -> Result<(), Self::Error> {
unreachable!()
}
}

impl<T: PrivateClaimSet> RemoveClaim<std::convert::Infallible> for T {
fn remove_claim(&mut self) -> Option<std::convert::Infallible> {
impl<T: PrivateClaimSet> RemoveClaim<NoClaim> for T {
fn remove_claim(&mut self) -> Option<NoClaim> {
None
}
}

impl<T: PrivateClaimSet> TryRemoveClaim<std::convert::Infallible> for T {
fn try_remove_claim(&mut self) -> Result<Option<std::convert::Infallible>, Self::Error> {
impl<T: PrivateClaimSet> TryRemoveClaim<NoClaim> for T {
fn try_remove_claim(&mut self) -> Result<Option<NoClaim>, Self::Error> {
Ok(None)
}
}
12 changes: 6 additions & 6 deletions crates/claims/crates/jwt/src/claims/registered.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Claim, JWTClaims};
use crate::{
ClaimSet, GetClaim, NumericDate, RemoveClaim, SetClaim, StringOrURI, TryGetClaim,
ClaimSet, GetClaim, NoClaim, NumericDate, RemoveClaim, SetClaim, StringOrURI, TryGetClaim,
TryRemoveClaim, TrySetClaim,
};
use ssi_claims_core::{ClaimsValidity, DateTimeEnvironment, Validate};
Expand All @@ -18,7 +18,7 @@ pub trait RegisteredClaim: Claim + Into<AnyRegisteredClaim> {
fn extract_mut(claim: &mut AnyRegisteredClaim) -> Option<&mut Self>;
}

impl RegisteredClaim for std::convert::Infallible {
impl RegisteredClaim for NoClaim {
const JWT_REGISTERED_CLAIM_KIND: RegisteredClaimKind = RegisteredClaimKind::Audience;

fn extract(_: AnyRegisteredClaim) -> Option<Self> {
Expand All @@ -34,8 +34,8 @@ impl RegisteredClaim for std::convert::Infallible {
}
}

impl From<std::convert::Infallible> for AnyRegisteredClaim {
fn from(_: std::convert::Infallible) -> Self {
impl From<NoClaim> for AnyRegisteredClaim {
fn from(_: NoClaim) -> Self {
unreachable!()
}
}
Expand Down Expand Up @@ -97,7 +97,7 @@ impl RegisteredClaims {
}

impl ClaimSet for RegisteredClaims {
type Error = std::convert::Infallible;
type Error = NoClaim;
}

impl JWSPayload for RegisteredClaims {
Expand Down Expand Up @@ -225,7 +225,7 @@ macro_rules! registered_claims {
pub struct $variant(pub $ty);

impl Claim for $variant {
type Private = std::convert::Infallible;
type Private = NoClaim;
type Registered = Self;

const JWT_CLAIM_NAME: &'static str = $name;
Expand Down

0 comments on commit 56fb310

Please sign in to comment.