Premium plans + session changes

This commit is contained in:
Brandon Thomas committed 2022-11-01 01:54:07 -04:00
1 parent e5db702289
commit 778d7eba7e
4 files changed
+53 -6

No files matched your search

@@ -16,11 +16,14 @@ CREATE TABLE user_subscriptions (
-- We want the latitude to correct mistakes in the future. (Hopefully this doesn't bite me later.)
maybe_user_token VARCHAR(32) DEFAULT NULL,
-- If we host multiple sites with distinct categories, this will enable us to segregate subscriptions.
-- If we host multiple sites with distinct categories, this will enable us to segregate
-- subscriptions. This is effectively a subscriptions namespace.
-- TODO: Rename 'namespace'
subscription_category VARCHAR(32) NOT NULL,
-- This is the identifier for the actual type of subscription.
-- These will not be defined in the database, but rather the source code.
-- TODO: Rename 'slug'
subscription_product_key VARCHAR(32) NOT NULL,
-- ========== STRIPE DATA ==========
@@ -48,6 +48,20 @@ pub struct UserMetadata {
/// Possible existing Stripe customer ID for the user.
pub maybe_existing_stripe_customer_id: Option<String>,
/// Existing subscriptions that the user has.
pub existing_subscription_keys: Vec<SubscriptionKey>,
}
pub struct SubscriptionKey {
/// The category or namespace for the product, eg "fakeyou" or "powerstream".
/// TODO: Rename "namespace"
pub internal_subscription_category: String,
/// The key for the product in our internal system (not a stripe id),
/// eg. "fakeyou_en_pro" or "stream_package_plus".
/// TODO: Rename "slug"
pub internal_subscription_product_key: String,
}
/// Allows us to inject a user lookup from the HTTP request's session info and database backend,
@@ -16,7 +16,13 @@ use sha2::Sha256;
use std::collections::BTreeMap;
use time::OffsetDateTime;
const COOKIE_VERSION : u32 = 1;
/**
* Cookie version history
*
* Version 1: Claims include "session_token" and "cookie_version"
* Version 2: The "user_token" is added to the claims, and the version is bumped to "2"
*/
const COOKIE_VERSION : u32 = 2;
const SESSION_COOKIE_NAME : &'static str = "session";
@@ -29,6 +35,15 @@ pub struct SessionCookieManager {
hmac_secret: String,
}
#[derive(Clone)]
pub struct SessionCookiePayload {
/// The database primary key for the session instance.
pub session_token: String,
/// The primary key identifier of the user.
/// Version 1 cookies do not have a user token, hence it is optional.
pub maybe_user_token: Option<String>,
}
impl SessionCookieManager {
pub fn new(cookie_domain: &str, hmac_secret: &str) -> Self {
Self {
@@ -37,7 +52,7 @@ impl SessionCookieManager {
}
}
pub fn create_cookie(&self, session_token: &str) -> AnyhowResult<Cookie> {
pub fn create_cookie(&self, session_token: &str, user_token: &str) -> AnyhowResult<Cookie> {
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
@@ -45,6 +60,7 @@ impl SessionCookieManager {
let mut claims = BTreeMap::new();
claims.insert("session_token", session_token);
claims.insert("user_token", user_token);
claims.insert("cookie_version", &cookie_version);
let jwt_string = claims.sign_with_key(&key)?;
@@ -69,17 +85,30 @@ impl SessionCookieManager {
.finish()
}
pub fn decode_session_token(&self, session_cookie: &Cookie) -> AnyhowResult<String> {
pub fn decode_session_cookie_payload(&self, session_cookie: &Cookie)
-> AnyhowResult<SessionCookiePayload>
{
let key: Hmac<Sha256> = Hmac::new_varkey(self.hmac_secret.as_bytes())
.map_err(|e| anyhow!("invalid hmac: {:?}", e))?;
.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();
let maybe_user_token = claims.get("user_token")
.map(|t| t.to_string());
Ok(session_token)
Ok(SessionCookiePayload {
session_token,
maybe_user_token,
})
}
pub fn decode_session_token(&self, session_cookie: &Cookie) -> AnyhowResult<String> {
let cookie_payload =
self.decode_session_cookie_payload(session_cookie)?;
Ok(cookie_payload.session_token)
}
pub fn decode_session_token_from_request(&self, request: &HttpRequest)
@@ -48,6 +48,7 @@ impl InternalUserLookup for StripeInternalUserLookupImpl {
username: Some(user_session.username),
user_email: Some(user_session.email_address),
maybe_existing_stripe_customer_id: user_session.maybe_stripe_customer_id,
existing_subscription_keys: vec![]
})),
}
}