206 lines
6.6 KiB
Rust
206 lines
6.6 KiB
Rust
use std::fmt::Display;
|
|
|
|
use crate::models::*;
|
|
use chrono::{DateTime, Utc};
|
|
|
|
#[derive(Debug)]
|
|
pub enum AuthErr {
|
|
UnknownSession,
|
|
UnknownUser,
|
|
InvalidPassword,
|
|
NotAnAdmin,
|
|
Other(Box<dyn std::error::Error + Sync + Send>),
|
|
}
|
|
|
|
impl Display for AuthErr {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::UnknownUser => write!(f, "unknown user"),
|
|
Self::UnknownSession => write!(f, "unknown session"),
|
|
Self::InvalidPassword => write!(f, "invalid password"),
|
|
Self::NotAnAdmin => write!(f, "not an admin"),
|
|
Self::Other(err) => err.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for AuthErr {}
|
|
|
|
/// API abstraction providing methods for serving the Gpodder API
|
|
pub trait GpodderStore:
|
|
GpodderAuthStore + GpodderDeviceStore + GpodderSubscriptionStore + GpodderEpisodeActionStore
|
|
{
|
|
}
|
|
|
|
impl<T> GpodderStore for T where
|
|
T: GpodderAuthStore + GpodderDeviceStore + GpodderSubscriptionStore + GpodderEpisodeActionStore
|
|
{
|
|
}
|
|
|
|
pub trait GpodderAuthStore {
|
|
/// Retrieve the session with the given session ID
|
|
fn get_session(&self, session_id: i64) -> Result<Option<Session>, AuthErr>;
|
|
|
|
/// Retrieve a paginated list of the given user's sessions, ordered descending by the last seen
|
|
/// value.
|
|
fn paginated_sessions(&self, user: &User, page: Page) -> Result<Vec<Session>, AuthErr>;
|
|
|
|
/// Retrieve the user with the given username
|
|
fn get_user(&self, username: &str) -> Result<Option<User>, AuthErr>;
|
|
|
|
/// Insert a new user into the data store
|
|
fn insert_user(&self, username: &str, password_hash: &str) -> Result<User, AuthErr>;
|
|
|
|
/// Create a new session for a user with the given session ID
|
|
///
|
|
/// The `last_seen` timestamp's precision should be at least accurate to the second
|
|
fn insert_session(&self, session: &Session) -> Result<(), AuthErr>;
|
|
|
|
/// Remove the session with the given session ID
|
|
fn remove_session(&self, session_id: i64) -> Result<(), AuthErr>;
|
|
|
|
/// Update the session's timestamp
|
|
fn refresh_session(&self, session: &Session, timestamp: DateTime<Utc>) -> Result<(), AuthErr>;
|
|
|
|
/// Remove any sessions whose last_seen timestamp is before the given minimum value
|
|
fn remove_old_sessions(&self, min_last_seen: DateTime<Utc>) -> Result<usize, AuthErr>;
|
|
|
|
/// Return the given page of users, ordered by username
|
|
fn paginated_users(&self, page: Page, filter: &UserFilter) -> Result<Vec<User>, AuthErr>;
|
|
|
|
/// Insert the signup link into the database.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// If a database failure occurs
|
|
fn insert_signup_link(&self, link: &SignupLink) -> Result<(), AuthErr>;
|
|
|
|
/// Get the signup link associated with the given ID
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// Some(link) if the ID corresponds to an existing signup link; None otherwise
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// If a database failure occurs
|
|
fn get_signup_link(&self, id: i64) -> Result<Option<SignupLink>, AuthErr>;
|
|
|
|
/// Remove the signup link with the given ID.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// True if the ID existed in the database; false otherwise
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// If a database failure occurs
|
|
fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr>;
|
|
}
|
|
|
|
pub trait GpodderDeviceStore {
|
|
/// Return all devices associated with the user
|
|
fn devices_for_user(&self, user: &User) -> Result<Vec<Device>, AuthErr>;
|
|
|
|
/// Update the information for the given device. If the device doesn't exist yet, it should be
|
|
/// created without a sync group.
|
|
fn update_device_info(
|
|
&self,
|
|
user: &User,
|
|
device_id: &str,
|
|
patch: DevicePatch,
|
|
) -> Result<(), AuthErr>;
|
|
|
|
/// Add the devices to the same sync group by:
|
|
///
|
|
/// * Merging the sync groups of all devices already in a sync group
|
|
/// * Adding all devices not yet in a sync group to the newly merged sync group
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// ID of the final sync group
|
|
fn merge_sync_groups(&self, user: &User, device_ids: Vec<&str>) -> Result<i64, AuthErr>;
|
|
|
|
/// Synchronize the sync group by adding or removing subscriptions to each device so that each
|
|
/// device's subscription state is the same
|
|
fn synchronize_sync_group(
|
|
&self,
|
|
group_id: i64,
|
|
time_changed: DateTime<Utc>,
|
|
) -> Result<(), AuthErr>;
|
|
|
|
/// Remove the devices from their respective sync groups
|
|
fn remove_from_sync_group(&self, user: &User, device_ids: Vec<&str>) -> Result<(), AuthErr>;
|
|
|
|
/// Return all device IDs for the user, grouped per sync group
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A tuple (unsynced devices, sync groups)
|
|
fn devices_by_sync_group(
|
|
&self,
|
|
user: &User,
|
|
) -> Result<(Vec<String>, Vec<Vec<String>>), AuthErr>;
|
|
}
|
|
|
|
pub trait GpodderSubscriptionStore {
|
|
/// Return the subscriptions for the given device
|
|
fn subscriptions_for_device(
|
|
&self,
|
|
user: &User,
|
|
device_id: &str,
|
|
) -> Result<Vec<Subscription>, AuthErr>;
|
|
|
|
/// Return all subscriptions for a given user
|
|
fn subscriptions_for_user(&self, user: &User) -> Result<Vec<Subscription>, AuthErr>;
|
|
|
|
/// Replace the list of subscriptions for a device and all devices in its sync group with the
|
|
/// given list
|
|
fn set_subscriptions_for_device(
|
|
&self,
|
|
user: &User,
|
|
device_id: &str,
|
|
urls: Vec<String>,
|
|
time_changed: DateTime<Utc>,
|
|
) -> Result<(), AuthErr>;
|
|
|
|
/// Update the list of subscriptions for a device and all devices in its sync group by adding
|
|
/// and removing the given URLs
|
|
fn update_subscriptions_for_device(
|
|
&self,
|
|
user: &User,
|
|
device_id: &str,
|
|
add: Vec<String>,
|
|
remove: Vec<String>,
|
|
time_changed: DateTime<Utc>,
|
|
) -> Result<(), AuthErr>;
|
|
|
|
/// Returns the changes in subscriptions since the given timestamp.
|
|
fn subscription_updates_for_device(
|
|
&self,
|
|
user: &User,
|
|
device_id: &str,
|
|
since: DateTime<Utc>,
|
|
) -> Result<(Vec<Subscription>, Vec<Subscription>), AuthErr>;
|
|
}
|
|
|
|
pub trait GpodderEpisodeActionStore {
|
|
/// Insert the given episode actions into the datastore.
|
|
fn add_episode_actions(
|
|
&self,
|
|
user: &User,
|
|
actions: Vec<EpisodeAction>,
|
|
time_changed: DateTime<Utc>,
|
|
) -> Result<(), AuthErr>;
|
|
|
|
/// Retrieve the list of episode actions for the given user.
|
|
fn episode_actions_for_user(
|
|
&self,
|
|
user: &User,
|
|
since: Option<DateTime<Utc>>,
|
|
podcast: Option<String>,
|
|
device: Option<String>,
|
|
aggregated: bool,
|
|
) -> Result<Vec<EpisodeAction>, AuthErr>;
|
|
}
|