attested_request/
reject.rs1use std::{error::Error, fmt};
4
5use http::StatusCode;
6
7use crate::profile::Platform;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub enum RejectReason {
17 HeadersMissing,
19 IntegrityTokenInvalid,
21 CreatedTooOld,
23 CreatedTooFarInFuture,
25 Replayed,
27 JwksUnavailable,
29 NonceStoreUnavailable,
31 BodyReadTimeout,
33 ClientDisconnected,
35 BodyReadFailed,
37 DeviceIntegrityFailed,
39 SignatureInvalid,
41 SignatureInputMalformed,
43 SignatureMalformed,
45 ComponentMissing,
47 AlgMismatch,
49 NonceInvalid,
51 SignatureBaseIncomplete,
53 BodyTooLarge,
55}
56
57impl RejectReason {
58 #[must_use]
60 pub const fn as_str(self) -> &'static str {
61 match self {
62 Self::HeadersMissing => "headers_missing",
63 Self::IntegrityTokenInvalid => "integrity_token_invalid",
64 Self::CreatedTooOld => "created_too_old",
65 Self::CreatedTooFarInFuture => "created_too_far_in_future",
66 Self::Replayed => "replayed",
67 Self::JwksUnavailable => "jwks_unavailable",
68 Self::NonceStoreUnavailable => "nonce_store_unavailable",
69 Self::BodyReadTimeout => "body_read_timeout",
70 Self::ClientDisconnected => "client_disconnected",
71 Self::BodyReadFailed => "body_read_failed",
72 Self::DeviceIntegrityFailed => "device_integrity_failed",
73 Self::SignatureInvalid => "signature_invalid",
74 Self::SignatureInputMalformed => "signature_input_malformed",
75 Self::SignatureMalformed => "signature_malformed",
76 Self::ComponentMissing => "component_missing",
77 Self::AlgMismatch => "alg_mismatch",
78 Self::NonceInvalid => "nonce_invalid",
79 Self::SignatureBaseIncomplete => "signature_base_incomplete",
80 Self::BodyTooLarge => "body_too_large",
81 }
82 }
83
84 #[must_use]
86 pub fn status(self) -> StatusCode {
87 match self {
88 Self::HeadersMissing
89 | Self::IntegrityTokenInvalid
90 | Self::CreatedTooOld
91 | Self::CreatedTooFarInFuture
92 | Self::Replayed
93 | Self::AlgMismatch => StatusCode::UNAUTHORIZED,
94 Self::DeviceIntegrityFailed | Self::SignatureInvalid => StatusCode::FORBIDDEN,
95 Self::SignatureInputMalformed
96 | Self::SignatureMalformed
97 | Self::ComponentMissing
98 | Self::NonceInvalid
99 | Self::SignatureBaseIncomplete => StatusCode::BAD_REQUEST,
100 Self::JwksUnavailable | Self::NonceStoreUnavailable => StatusCode::SERVICE_UNAVAILABLE,
101 Self::BodyReadTimeout => StatusCode::REQUEST_TIMEOUT,
102 Self::BodyTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
103 Self::BodyReadFailed => StatusCode::INTERNAL_SERVER_ERROR,
104 Self::ClientDisconnected => {
106 StatusCode::from_u16(499).expect("499 is a valid status code")
107 }
108 }
109 }
110}
111
112impl fmt::Display for RejectReason {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 f.write_str(self.as_str())
115 }
116}
117
118#[derive(Debug)]
123pub struct Rejection {
124 pub reason: RejectReason,
126 pub platform: Option<Platform>,
128 pub source: Option<Box<dyn Error + Send + Sync>>,
130}
131
132impl Rejection {
133 #[must_use]
135 pub const fn new(reason: RejectReason) -> Self {
136 Self {
137 reason,
138 platform: None,
139 source: None,
140 }
141 }
142
143 #[must_use]
145 pub fn with_source(mut self, source: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
146 self.source = Some(source.into());
147 self
148 }
149
150 #[must_use]
152 pub const fn with_platform(mut self, platform: Platform) -> Self {
153 self.platform = Some(platform);
154 self
155 }
156}
157
158impl From<RejectReason> for Rejection {
159 fn from(reason: RejectReason) -> Self {
160 Self::new(reason)
161 }
162}
163
164impl fmt::Display for Rejection {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "request rejected: {}", self.reason)?;
167 if let Some(source) = &self.source {
168 write!(f, ": {source}")?;
169 }
170 Ok(())
171 }
172}
173
174impl Error for Rejection {
175 fn source(&self) -> Option<&(dyn Error + 'static)> {
176 self.source.as_deref().map(|source| source as _)
177 }
178}