Compare commits
No commits in common. "320a46c0f313e6ba6f048f94d6635ac42362bc6e" and "2249d986eb58a5123999b225a87e85150588f837" have entirely different histories.
320a46c0f3
...
2249d986eb
|
@ -12,8 +12,6 @@ create table sessions (
|
||||||
references users (id)
|
references users (id)
|
||||||
on delete cascade,
|
on delete cascade,
|
||||||
|
|
||||||
last_seen bigint not null,
|
|
||||||
|
|
||||||
unique (id, user_id)
|
unique (id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
alter table sessions
|
||||||
|
drop column last_seen;
|
|
@ -0,0 +1,3 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
alter table sessions
|
||||||
|
add column last_seen bigint not null default 0;
|
|
@ -1,5 +1,6 @@
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
use super::SqliteRepository;
|
use super::SqliteRepository;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -29,6 +30,113 @@ impl From<db::User> for gpodder::User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl gpodder::AuthRepository for SqliteRepository {
|
||||||
|
fn validate_credentials(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<gpodder::models::User, AuthErr> {
|
||||||
|
if let Some(user) = users::table
|
||||||
|
.select(db::User::as_select())
|
||||||
|
.filter(users::username.eq(username))
|
||||||
|
.first(&mut self.pool.get()?)
|
||||||
|
.optional()?
|
||||||
|
{
|
||||||
|
if user.verify_password(password) {
|
||||||
|
Ok(gpodder::User {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
password_hash: user.password_hash,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(gpodder::AuthErr::InvalidPassword)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(gpodder::AuthErr::UnknownUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_session(&self, session_id: i64) -> Result<gpodder::User, gpodder::AuthErr> {
|
||||||
|
match sessions::dsl::sessions
|
||||||
|
.inner_join(users::table)
|
||||||
|
.filter(sessions::id.eq(session_id))
|
||||||
|
.select(db::User::as_select())
|
||||||
|
.get_result(&mut self.pool.get()?)
|
||||||
|
{
|
||||||
|
Ok(user) => Ok(gpodder::User {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
password_hash: user.password_hash,
|
||||||
|
}),
|
||||||
|
Err(diesel::result::Error::NotFound) => Err(gpodder::AuthErr::UnknownSession),
|
||||||
|
Err(err) => Err(gpodder::AuthErr::Other(Box::new(err))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_session(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<(i64, gpodder::models::User), gpodder::AuthErr> {
|
||||||
|
if let Some(user) = users::table
|
||||||
|
.select(db::User::as_select())
|
||||||
|
.filter(users::username.eq(username))
|
||||||
|
.first(&mut self.pool.get()?)
|
||||||
|
.optional()?
|
||||||
|
{
|
||||||
|
if user.verify_password(password) {
|
||||||
|
let id: i64 = rand::thread_rng().gen();
|
||||||
|
|
||||||
|
let session_id = db::Session {
|
||||||
|
id,
|
||||||
|
user_id: user.id,
|
||||||
|
last_seen: chrono::Utc::now().timestamp(),
|
||||||
|
}
|
||||||
|
.insert_into(sessions::table)
|
||||||
|
.returning(sessions::id)
|
||||||
|
.get_result(&mut self.pool.get()?)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
session_id,
|
||||||
|
gpodder::User {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
password_hash: user.password_hash,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(gpodder::AuthErr::InvalidPassword)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(gpodder::AuthErr::UnknownUser)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_session(&self, username: &str, session_id: i64) -> Result<(), gpodder::AuthErr> {
|
||||||
|
let conn = &mut self.pool.get()?;
|
||||||
|
|
||||||
|
if let Some(user) = sessions::table
|
||||||
|
.inner_join(users::table)
|
||||||
|
.filter(sessions::id.eq(session_id))
|
||||||
|
.select(db::User::as_select())
|
||||||
|
.get_result(conn)
|
||||||
|
.optional()?
|
||||||
|
{
|
||||||
|
if user.username == username {
|
||||||
|
Ok(
|
||||||
|
diesel::delete(sessions::table.filter(sessions::id.eq(session_id)))
|
||||||
|
.execute(conn)
|
||||||
|
.map(|_| ())?,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Err(AuthErr::UnknownUser)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl gpodder::AuthStore for SqliteRepository {
|
impl gpodder::AuthStore for SqliteRepository {
|
||||||
fn get_user(&self, username: &str) -> Result<Option<gpodder::models::User>, AuthErr> {
|
fn get_user(&self, username: &str) -> Result<Option<gpodder::models::User>, AuthErr> {
|
||||||
Ok(users::table
|
Ok(users::table
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
use super::SqliteRepository;
|
use super::SqliteRepository;
|
||||||
|
@ -96,35 +95,4 @@ impl gpodder::DeviceRepository for SqliteRepository {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_sync_groups(
|
|
||||||
&self,
|
|
||||||
user: &gpodder::User,
|
|
||||||
device_ids: Vec<&str>,
|
|
||||||
) -> Result<i64, gpodder::AuthErr> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_from_sync_group(
|
|
||||||
&self,
|
|
||||||
user: &gpodder::User,
|
|
||||||
device_ids: Vec<&str>,
|
|
||||||
) -> Result<(), gpodder::AuthErr> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn synchronize_sync_group(
|
|
||||||
&self,
|
|
||||||
group_id: i64,
|
|
||||||
time_changed: DateTime<Utc>,
|
|
||||||
) -> Result<(), gpodder::AuthErr> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn devices_by_sync_group(
|
|
||||||
&self,
|
|
||||||
user: &gpodder::User,
|
|
||||||
) -> Result<(Vec<gpodder::Device>, Vec<Vec<gpodder::Device>>), gpodder::AuthErr> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,24 @@ impl<T> Store for T where
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait AuthRepository {
|
||||||
|
/// Validate the given session ID and return its user.
|
||||||
|
fn validate_session(&self, session_id: i64) -> Result<models::User, AuthErr>;
|
||||||
|
|
||||||
|
/// Validate the credentials, returning the user if the credentials are correct.
|
||||||
|
fn validate_credentials(&self, username: &str, password: &str)
|
||||||
|
-> Result<models::User, AuthErr>;
|
||||||
|
|
||||||
|
/// Create a new session for the given user.
|
||||||
|
fn create_session(
|
||||||
|
&self,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<(i64, models::User), AuthErr>;
|
||||||
|
|
||||||
|
fn remove_session(&self, username: &str, session_id: i64) -> Result<(), AuthErr>;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait AuthStore {
|
pub trait AuthStore {
|
||||||
/// Retrieve the session with the given session ID
|
/// Retrieve the session with the given session ID
|
||||||
fn get_session(&self, session_id: i64) -> Result<Option<models::Session>, AuthErr>;
|
fn get_session(&self, session_id: i64) -> Result<Option<models::Session>, AuthErr>;
|
||||||
|
@ -63,44 +81,13 @@ pub trait DeviceRepository {
|
||||||
fn devices_for_user(&self, user: &User) -> Result<Vec<Device>, AuthErr>;
|
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
|
/// Update the information for the given device. If the device doesn't exist yet, it should be
|
||||||
/// created without a sync group.
|
/// created.
|
||||||
fn update_device_info(
|
fn update_device_info(
|
||||||
&self,
|
&self,
|
||||||
user: &User,
|
user: &User,
|
||||||
device_id: &str,
|
device_id: &str,
|
||||||
patch: DevicePatch,
|
patch: DevicePatch,
|
||||||
) -> Result<(), AuthErr>;
|
) -> 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 devices for the user, grouped per sync group
|
|
||||||
///
|
|
||||||
/// # Returns
|
|
||||||
///
|
|
||||||
/// A tuple (unsynced devices, sync groups)
|
|
||||||
fn devices_by_sync_group(
|
|
||||||
&self,
|
|
||||||
user: &User,
|
|
||||||
) -> Result<(Vec<Device>, Vec<Vec<Device>>), AuthErr>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SubscriptionRepository {
|
pub trait SubscriptionRepository {
|
||||||
|
@ -114,8 +101,7 @@ pub trait SubscriptionRepository {
|
||||||
/// Return all subscriptions for a given user
|
/// Return all subscriptions for a given user
|
||||||
fn subscriptions_for_user(&self, user: &User) -> Result<Vec<models::Subscription>, AuthErr>;
|
fn subscriptions_for_user(&self, user: &User) -> Result<Vec<models::Subscription>, AuthErr>;
|
||||||
|
|
||||||
/// Replace the list of subscriptions for a device and all devices in its sync group with the
|
/// Replace the list of subscriptions for a device with the given list
|
||||||
/// given list
|
|
||||||
fn set_subscriptions_for_device(
|
fn set_subscriptions_for_device(
|
||||||
&self,
|
&self,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
@ -124,8 +110,7 @@ pub trait SubscriptionRepository {
|
||||||
time_changed: DateTime<Utc>,
|
time_changed: DateTime<Utc>,
|
||||||
) -> Result<(), AuthErr>;
|
) -> Result<(), AuthErr>;
|
||||||
|
|
||||||
/// Update the list of subscriptions for a device and all devices in its sync group by adding
|
/// Update the list of subscriptions for a device by adding and removing the given URLs
|
||||||
/// and removing the given URLs
|
|
||||||
fn update_subscriptions_for_device(
|
fn update_subscriptions_for_device(
|
||||||
&self,
|
&self,
|
||||||
user: &User,
|
user: &User,
|
||||||
|
|
|
@ -94,22 +94,6 @@ impl GpodderRepository {
|
||||||
self.store.update_device_info(user, device_id, patch)
|
self.store.update_device_info(user, device_id, patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_device_sync_status(
|
|
||||||
&self,
|
|
||||||
user: &models::User,
|
|
||||||
sync: Vec<Vec<&str>>,
|
|
||||||
unsync: Vec<&str>,
|
|
||||||
) -> Result<(), AuthErr> {
|
|
||||||
todo!("perform diff devices to sync and unsync")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn devices_by_sync_group(
|
|
||||||
&self,
|
|
||||||
user: &models::User,
|
|
||||||
) -> Result<(Vec<models::Device>, Vec<Vec<models::Device>>), AuthErr> {
|
|
||||||
self.store.devices_by_sync_group(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subscriptions_for_device(
|
pub fn subscriptions_for_device(
|
||||||
&self,
|
&self,
|
||||||
user: &models::User,
|
user: &models::User,
|
||||||
|
|
Loading…
Reference in New Issue