Compare commits
	
		
			2 Commits 
		
	
	
		
			2514aa8413
			...
			5cd1f4f736
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						5cd1f4f736 | |
| 
							
							
								
									
								
								 | 
						c48d2a78ca | 
| 
						 | 
					@ -77,3 +77,9 @@ pub struct Page {
 | 
				
			||||||
pub struct UserFilter {
 | 
					pub struct UserFilter {
 | 
				
			||||||
    pub username: Option<String>,
 | 
					    pub username: Option<String>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub struct SignupLink {
 | 
				
			||||||
 | 
					    pub id: i64,
 | 
				
			||||||
 | 
					    pub time_created: DateTime<Utc>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,12 @@
 | 
				
			||||||
 | 
					use chrono::Utc;
 | 
				
			||||||
 | 
					use rand::Rng;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{AuthErr, Page, models};
 | 
					use crate::{AuthErr, Page, models};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Admin view of the repository, providing methods only allowed by admins
 | 
					/// Admin view of the repository, providing methods only allowed by admins
 | 
				
			||||||
pub struct AdminRepository<'a> {
 | 
					pub struct AdminRepository<'a> {
 | 
				
			||||||
    pub(crate) store: &'a (dyn super::GpodderStore + Send + Sync),
 | 
					    pub(crate) store: &'a (dyn super::GpodderStore + Send + Sync),
 | 
				
			||||||
    pub(crate) user: &'a models::User,
 | 
					    pub(crate) _user: &'a models::User,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a> AdminRepository<'a> {
 | 
					impl<'a> AdminRepository<'a> {
 | 
				
			||||||
| 
						 | 
					@ -14,4 +17,21 @@ impl<'a> AdminRepository<'a> {
 | 
				
			||||||
    ) -> Result<Vec<models::User>, AuthErr> {
 | 
					    ) -> Result<Vec<models::User>, AuthErr> {
 | 
				
			||||||
        self.store.paginated_users(page, filter)
 | 
					        self.store.paginated_users(page, filter)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Generate a new unique signup link ID
 | 
				
			||||||
 | 
					    pub fn generate_signup_link(&self) -> Result<models::SignupLink, AuthErr> {
 | 
				
			||||||
 | 
					        let link = models::SignupLink {
 | 
				
			||||||
 | 
					            id: rand::thread_rng().r#gen(),
 | 
				
			||||||
 | 
					            time_created: Utc::now(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.store.insert_signup_link(&link)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(link)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Remove the signup link with the given ID, if it exists
 | 
				
			||||||
 | 
					    pub fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> {
 | 
				
			||||||
 | 
					        self.store.remove_signup_link(id)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ impl GpodderRepository {
 | 
				
			||||||
        if user.admin {
 | 
					        if user.admin {
 | 
				
			||||||
            Ok(admin::AdminRepository {
 | 
					            Ok(admin::AdminRepository {
 | 
				
			||||||
                store: self.store.as_ref(),
 | 
					                store: self.store.as_ref(),
 | 
				
			||||||
                user,
 | 
					                _user: user,
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Err(AuthErr::NotAnAdmin)
 | 
					            Err(AuthErr::NotAnAdmin)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,35 @@ pub trait GpodderAuthStore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Return the given page of users, ordered by username
 | 
					    /// Return the given page of users, ordered by username
 | 
				
			||||||
    fn paginated_users(&self, page: Page, filter: &UserFilter) -> Result<Vec<User>, AuthErr>;
 | 
					    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 {
 | 
					pub trait GpodderDeviceStore {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,8 +15,15 @@ tracing = { workspace = true }
 | 
				
			||||||
chrono = { workspace = true, features = ["serde"] }
 | 
					chrono = { workspace = true, features = ["serde"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
 | 
					libsqlite3-sys = { version = "0.31.0", features = ["bundled"] }
 | 
				
			||||||
diesel = { version = "2.2.7", features = ["r2d2", "sqlite", "returning_clauses_for_sqlite_3_35"] }
 | 
					 | 
				
			||||||
diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
 | 
					diesel_migrations = { version = "2.2.0", features = ["sqlite"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[dependencies.diesel]
 | 
				
			||||||
 | 
					version = "2.2.7"
 | 
				
			||||||
 | 
					features = [
 | 
				
			||||||
 | 
					    "r2d2",
 | 
				
			||||||
 | 
					    "sqlite",
 | 
				
			||||||
 | 
					    "returning_clauses_for_sqlite_3_35",
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
criterion = "0.5.1"
 | 
					criterion = "0.5.1"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					-- This file should undo anything in `up.sql`
 | 
				
			||||||
 | 
					drop table signup_links;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					-- Your SQL goes here
 | 
				
			||||||
 | 
					create table signup_links (
 | 
				
			||||||
 | 
					    id bigint primary key not null,
 | 
				
			||||||
 | 
					    created_at bigint not null
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
| 
						 | 
					@ -2,5 +2,6 @@ pub mod device;
 | 
				
			||||||
pub mod device_subscription;
 | 
					pub mod device_subscription;
 | 
				
			||||||
pub mod episode_action;
 | 
					pub mod episode_action;
 | 
				
			||||||
pub mod session;
 | 
					pub mod session;
 | 
				
			||||||
 | 
					pub mod signup_link;
 | 
				
			||||||
pub mod sync_group;
 | 
					pub mod sync_group;
 | 
				
			||||||
pub mod user;
 | 
					pub mod user;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					use diesel::prelude::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use crate::schema::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Queryable, Selectable, Insertable)]
 | 
				
			||||||
 | 
					#[diesel(table_name = signup_links)]
 | 
				
			||||||
 | 
					#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
 | 
				
			||||||
 | 
					pub struct SignupLink {
 | 
				
			||||||
 | 
					    pub id: i64,
 | 
				
			||||||
 | 
					    pub created_at: i64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -7,6 +7,7 @@ use crate::{
 | 
				
			||||||
    DbError,
 | 
					    DbError,
 | 
				
			||||||
    models::{
 | 
					    models::{
 | 
				
			||||||
        session::Session,
 | 
					        session::Session,
 | 
				
			||||||
 | 
					        signup_link::SignupLink,
 | 
				
			||||||
        user::{NewUser, User},
 | 
					        user::{NewUser, User},
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    schema::*,
 | 
					    schema::*,
 | 
				
			||||||
| 
						 | 
					@ -23,6 +24,15 @@ impl From<User> for gpodder::User {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl From<SignupLink> for gpodder::SignupLink {
 | 
				
			||||||
 | 
					    fn from(value: SignupLink) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            id: value.id,
 | 
				
			||||||
 | 
					            time_created: DateTime::from_timestamp(value.created_at, 0).unwrap(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl gpodder::GpodderAuthStore for SqliteRepository {
 | 
					impl gpodder::GpodderAuthStore 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
 | 
				
			||||||
| 
						 | 
					@ -170,4 +180,39 @@ impl gpodder::GpodderAuthStore for SqliteRepository {
 | 
				
			||||||
        })()
 | 
					        })()
 | 
				
			||||||
        .map_err(AuthErr::from)
 | 
					        .map_err(AuthErr::from)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn get_signup_link(&self, id: i64) -> Result<Option<gpodder::SignupLink>, AuthErr> {
 | 
				
			||||||
 | 
					        match signup_links::table
 | 
				
			||||||
 | 
					            .find(id)
 | 
				
			||||||
 | 
					            .select(SignupLink::as_select())
 | 
				
			||||||
 | 
					            .first(&mut self.pool.get().map_err(DbError::from)?)
 | 
				
			||||||
 | 
					            .optional()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Ok(Some(link)) => Ok(Some(gpodder::SignupLink::from(link))),
 | 
				
			||||||
 | 
					            Ok(None) => Ok(None),
 | 
				
			||||||
 | 
					            Err(err) => Err(DbError::from(err).into()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn insert_signup_link(&self, link: &gpodder::SignupLink) -> Result<(), AuthErr> {
 | 
				
			||||||
 | 
					        diesel::insert_into(signup_links::table)
 | 
				
			||||||
 | 
					            .values(SignupLink {
 | 
				
			||||||
 | 
					                id: link.id,
 | 
				
			||||||
 | 
					                created_at: link.time_created.timestamp(),
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .execute(&mut self.pool.get().map_err(DbError::from)?)
 | 
				
			||||||
 | 
					            .map_err(DbError::from)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn remove_signup_link(&self, id: i64) -> Result<bool, AuthErr> {
 | 
				
			||||||
 | 
					        match diesel::delete(signup_links::table.filter(signup_links::id.eq(id)))
 | 
				
			||||||
 | 
					            .execute(&mut self.pool.get().map_err(DbError::from)?)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            Ok(0) => Ok(false),
 | 
				
			||||||
 | 
					            Ok(_) => Ok(true),
 | 
				
			||||||
 | 
					            Err(err) => Err(DbError::from(err).into()),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,13 @@ diesel::table! {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diesel::table! {
 | 
				
			||||||
 | 
					    signup_links (id) {
 | 
				
			||||||
 | 
					        id -> BigInt,
 | 
				
			||||||
 | 
					        created_at -> BigInt,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
diesel::table! {
 | 
					diesel::table! {
 | 
				
			||||||
    sync_groups (id) {
 | 
					    sync_groups (id) {
 | 
				
			||||||
        id -> BigInt,
 | 
					        id -> BigInt,
 | 
				
			||||||
| 
						 | 
					@ -74,6 +81,7 @@ diesel::allow_tables_to_appear_in_same_query!(
 | 
				
			||||||
    devices,
 | 
					    devices,
 | 
				
			||||||
    episode_actions,
 | 
					    episode_actions,
 | 
				
			||||||
    sessions,
 | 
					    sessions,
 | 
				
			||||||
 | 
					    signup_links,
 | 
				
			||||||
    sync_groups,
 | 
					    sync_groups,
 | 
				
			||||||
    users,
 | 
					    users,
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue