mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
Premium plans + session changes
This commit is contained in:
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![]
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user