mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-09 03:21:23 +00:00
97 lines
2.7 KiB
Rust
97 lines
2.7 KiB
Rust
use actix_web::cookie::{Cookie, SameSite};
|
|
use actix_web::{HttpRequest, HttpMessage};
|
|
use anyhow::anyhow;
|
|
use crate::AnyhowResult;
|
|
use hmac::Hmac;
|
|
use hmac::NewMac;
|
|
use jwt::SignWithKey;
|
|
use jwt::VerifyWithKey;
|
|
use log::{info, warn};
|
|
use sha2::Sha256;
|
|
use std::collections::BTreeMap;
|
|
use std::ops::Sub;
|
|
use time::OffsetDateTime;
|
|
use log::LevelFilter::Off;
|
|
|
|
const COOKIE_VERSION : u32 = 1;
|
|
|
|
const SESSION_COOKIE_NAME : &'static str = "session";
|
|
|
|
#[derive(Clone)]
|
|
pub struct CookieManager {
|
|
cookie_domain: String,
|
|
hmac_secret: String,
|
|
}
|
|
|
|
impl CookieManager {
|
|
pub fn new(cookie_domain: &str, hmac_secret: &str) -> Self {
|
|
Self {
|
|
cookie_domain: cookie_domain.to_string(),
|
|
hmac_secret: hmac_secret.to_string(),
|
|
}
|
|
}
|
|
|
|
pub fn create_cookie(&self, session_token: &str) -> AnyhowResult<Cookie> {
|
|
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
|
|
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
|
|
|
|
let cookie_version = COOKIE_VERSION.to_string();
|
|
|
|
let mut claims = BTreeMap::new();
|
|
claims.insert("session_token", session_token);
|
|
claims.insert("cookie_version", &cookie_version);
|
|
|
|
let jwt_string = claims.sign_with_key(&key)?;
|
|
|
|
let make_secure = !self.cookie_domain.to_lowercase().contains("jungle.horse")
|
|
&& !self.cookie_domain.to_lowercase().contains("localhost");
|
|
|
|
Ok(Cookie::build(SESSION_COOKIE_NAME, jwt_string)
|
|
//.domain(&self.cookie_domain)
|
|
.secure(make_secure) // HTTPS-only
|
|
//.path("/")
|
|
//.http_only(true) // Not exposed to Javascript
|
|
//.expires(OffsetDateTime::now_utc() + time::Duration::days(365))
|
|
.permanent()
|
|
//.same_site(SameSite::Lax)
|
|
.finish())
|
|
}
|
|
|
|
pub fn delete_cookie(&self) -> Cookie {
|
|
Cookie::build(SESSION_COOKIE_NAME, "DELETED")
|
|
.expires(OffsetDateTime::unix_epoch())
|
|
.finish()
|
|
}
|
|
|
|
pub fn decode_session_token(&self, session_cookie: &Cookie) -> AnyhowResult<String> {
|
|
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
|
|
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
|
|
|
|
let cookie_contents = session_cookie.value().to_string();
|
|
|
|
let claims: BTreeMap<String, String> = cookie_contents.verify_with_key(&key)?;
|
|
|
|
let session_token = claims["session_token"].clone();
|
|
|
|
Ok(session_token)
|
|
}
|
|
|
|
pub fn decode_session_token_from_request(&self, request: &HttpRequest)
|
|
-> AnyhowResult<Option<String>>
|
|
{
|
|
let cookie = match request.cookie(SESSION_COOKIE_NAME) {
|
|
None => return Ok(None),
|
|
Some(cookie) => cookie,
|
|
};
|
|
|
|
match self.decode_session_token(&cookie) {
|
|
Err(e) => {
|
|
warn!("Session cookie decode error: {:?}", e);
|
|
Err(anyhow!("Could not decode session cookie: {:?}", e))
|
|
},
|
|
Ok(payload) => Ok(Some(payload)),
|
|
}
|
|
}
|
|
}
|
|
|