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