use cookie::Cookie;
use cookie::CookieJar;
use cookie::Key;
use http::HeaderMap;
use http::StatusCode;
use http::header::COOKIE;
use http::request::Parts;
use crate::extractors::FromRequest;
use crate::extractors::FromRequestParts;
use crate::responder::Responder;
use crate::types::Request;
pub struct CookieSigned {
jar: CookieJar,
key: Key,
}
#[derive(Debug)]
pub enum CookieSignedError {
MissingKey,
InvalidKey,
VerificationFailed(String),
InvalidCookieFormat,
InvalidSignature(String),
}
impl Responder for CookieSignedError {
fn into_response(self) -> crate::types::Response {
match self {
CookieSignedError::MissingKey => (
StatusCode::INTERNAL_SERVER_ERROR,
"Signed cookie master key not found in request extensions",
)
.into_response(),
CookieSignedError::InvalidKey => (
StatusCode::INTERNAL_SERVER_ERROR,
"Invalid signed cookie master key",
)
.into_response(),
CookieSignedError::VerificationFailed(err) => (
StatusCode::BAD_REQUEST,
format!("Failed to verify signed cookie: {err}"),
)
.into_response(),
CookieSignedError::InvalidCookieFormat => {
(StatusCode::BAD_REQUEST, "Invalid cookie format in request").into_response()
}
CookieSignedError::InvalidSignature(cookie_name) => (
StatusCode::BAD_REQUEST,
format!("Invalid signature for cookie: {cookie_name}"),
)
.into_response(),
}
}
}
impl CookieSigned {
pub fn new(key: Key) -> Self {
Self {
jar: CookieJar::new(),
key,
}
}
pub fn from_headers(headers: &HeaderMap, key: Key) -> Self {
let mut jar = CookieJar::new();
if let Some(cookie_header) = headers.get(COOKIE).and_then(|v| v.to_str().ok()) {
for cookie_str in cookie_header.split(';') {
if let Ok(cookie) = Cookie::parse(cookie_str.trim()) {
jar.add_original(cookie.into_owned());
}
}
}
Self { jar, key }
}
pub fn add(&mut self, cookie: Cookie<'static>) {
self.jar.signed_mut(&self.key).add(cookie);
}
pub fn remove(&mut self, name: &str) {
self
.jar
.signed_mut(&self.key)
.remove(Cookie::from(name.to_owned()));
}
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
self.jar.signed(&self.key).get(name)
}
pub fn inner(&self) -> &CookieJar {
&self.jar
}
pub fn inner_mut(&mut self) -> &mut CookieJar {
&mut self.jar
}
pub fn verify(&self, name: &str) -> bool {
self.get(name).is_some()
}
pub fn get_value(&self, name: &str) -> Option<String> {
self.get(name).map(|cookie| cookie.value().to_string())
}
pub fn contains(&self, name: &str) -> bool {
self.get(name).is_some()
}
pub fn key(&self) -> &Key {
&self.key
}
fn extract_from_request(req: &Request) -> Result<Self, CookieSignedError> {
let key = req
.extensions()
.get::<Key>()
.ok_or(CookieSignedError::MissingKey)?
.clone();
Ok(Self::from_headers(req.headers(), key))
}
fn extract_from_parts(parts: &Parts) -> Result<Self, CookieSignedError> {
let key = parts
.extensions
.get::<Key>()
.ok_or(CookieSignedError::MissingKey)?
.clone();
Ok(Self::from_headers(&parts.headers, key))
}
}
impl<'a> FromRequest<'a> for CookieSigned {
type Error = CookieSignedError;
fn from_request(
req: &'a mut Request,
) -> impl core::future::Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a {
futures_util::future::ready(Self::extract_from_request(req))
}
}
impl<'a> FromRequestParts<'a> for CookieSigned {
type Error = CookieSignedError;
fn from_request_parts(
parts: &'a mut Parts,
) -> impl core::future::Future<Output = core::result::Result<Self, Self::Error>> + Send + 'a {
futures_util::future::ready(Self::extract_from_parts(parts))
}
}