Skip to content

Commit

Permalink
Add comments
Browse files Browse the repository at this point in the history
Signed-off-by: Elizabeth Myers <[email protected]>
  • Loading branch information
Elizafox committed Mar 18, 2024
1 parent b39b05c commit 1a4f3dd
Show file tree
Hide file tree
Showing 28 changed files with 159 additions and 46 deletions.
2 changes: 2 additions & 0 deletions entity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# You should have received a copy of the CC0 legalcode along with this
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

# sea-orm entities for ShadyURL, largely automatically generated.

[package]
name = "entity"
version = "0.1.0"
Expand Down
2 changes: 2 additions & 0 deletions migration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# You should have received a copy of the CC0 legalcode along with this
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

# sea-orm database migrations, partially autogenerated.

[package]
name = "migration"
version = "0.1.0"
Expand Down
4 changes: 2 additions & 2 deletions service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@
# You should have received a copy of the CC0 legalcode along with this
# work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

# This contains a bunch of database services/abstractions for ShadyURL.

[package]
name = "service"
version = "0.1.0"
edition = "2021"
license = "CC0-1.0"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
name = "service"
path = "src/lib.rs"
Expand Down
4 changes: 4 additions & 0 deletions service/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/

// Database connection services.

use sea_orm::{ConnectOptions, DbConn, DbErr};
use tracing::log::LevelFilter;

Expand All @@ -20,6 +22,7 @@ use migration::{Migrator, MigratorTrait};
pub struct Database;

impl Database {
// Get a DbConn with the given connection options.
pub async fn get_with_connect_options(opt: ConnectOptions) -> Result<DbConn, DbErr> {
let db = sea_orm::Database::connect(opt).await?;

Expand All @@ -28,6 +31,7 @@ impl Database {
Ok(db)
}

// Get a DbConn with some default options.
pub async fn get(url: &str) -> Result<DbConn, DbErr> {
let mut opt = ConnectOptions::new(url);
opt.sqlx_logging(false)
Expand Down
11 changes: 11 additions & 0 deletions service/src/mutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/

// Database mutation operations for ShadyURL

use ipnetwork::{IpNetwork, Ipv6Network};
use sea_orm::*;

Expand All @@ -22,6 +24,7 @@ use crate::Query;
pub struct Mutation;

impl Mutation {
// Create a CIDR ban given a network, reason, and user
pub async fn create_cidr_ban(
db: &DbConn,
network: IpNetwork,
Expand Down Expand Up @@ -51,6 +54,7 @@ impl Mutation {
.await
}

// Create a user given a username and password hash
pub async fn create_user(
db: &DbConn,
username: &str,
Expand All @@ -65,6 +69,7 @@ impl Mutation {
.await
}

// Create a URL given a url, shady "filename", and IP
pub async fn create_url(
db: &DbConn,
url: &str,
Expand All @@ -81,6 +86,7 @@ impl Mutation {
.await
}

// Create a URL filter given a filter string, an optional reason, and a user.
pub async fn create_url_filter(
db: &DbConn,
filter: String,
Expand All @@ -97,6 +103,7 @@ impl Mutation {
.await
}

// Change a user password given a username and password hash.
pub async fn change_user_password(
db: &DbConn,
username: &str,
Expand All @@ -111,18 +118,22 @@ impl Mutation {
user.update(db).await.map(Into::into)
}

// Delete a CIDR ban by ID.
pub async fn delete_cidr_ban(db: &DbConn, id: i64) -> Result<DeleteResult, DbErr> {
CidrBan::delete_by_id(id).exec(db).await
}

// Delete a user by ID.
pub async fn delete_user(db: &DbConn, id: i64) -> Result<DeleteResult, DbErr> {
User::delete_by_id(id).exec(db).await
}

// Delete a URL by ID.
pub async fn delete_url(db: &DbConn, id: i64) -> Result<DeleteResult, DbErr> {
Url::delete_by_id(id).exec(db).await
}

// Delete a URL filter by ID.
pub async fn delete_url_filter(db: &DbConn, id: i64) -> Result<DeleteResult, DbErr> {
UrlFilter::delete_by_id(id).exec(db).await
}
Expand Down
19 changes: 18 additions & 1 deletion service/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/

// Database query operations for ShadyURL.

use std::net::IpAddr;

use sea_orm::*;
Expand All @@ -21,14 +23,17 @@ use ::entity::{cidr_ban, prelude::*, url, url_filter, user};
pub struct Query;

impl Query {
// Find a CIDR ban by ID.
pub async fn find_cidr_ban(db: &DbConn, id: i64) -> Result<Option<cidr_ban::Model>, DbErr> {
CidrBan::find_by_id(id).one(db).await
}

// Find a user by ID.
pub async fn find_user_by_id(db: &DbConn, id: i64) -> Result<Option<user::Model>, DbErr> {
User::find_by_id(id).one(db).await
}

// Find a user by username.
pub async fn find_user_by_username(
db: &DbConn,
username: &str,
Expand All @@ -39,10 +44,12 @@ impl Query {
.await
}

// Find a URL by its pointer.
pub async fn find_url_by_string(db: &DbConn, url: &str) -> Result<Vec<url::Model>, DbErr> {
Url::find().filter(url::Column::Url.eq(url)).all(db).await
}

// Find a URL by its shady filename.
pub async fn find_url_by_shady_string(
db: &DbConn,
shady: &str,
Expand All @@ -53,18 +60,25 @@ impl Query {
.await
}

// Find a URL by its ID.
pub async fn find_url_by_id(db: &DbConn, id: i64) -> Result<Option<url::Model>, DbErr> {
Url::find_by_id(id).one(db).await
}

// Get all URL's in the database.
// TODO: pagination?
pub async fn fetch_all_urls(db: &DbConn) -> Result<Vec<url::Model>, DbErr> {
Url::find().order_by_asc(url::Column::Id).all(db).await
}


// Find a URL filter by its ID.
pub async fn find_url_filter(db: &DbConn, id: i64) -> Result<Option<url_filter::Model>, DbErr> {
UrlFilter::find_by_id(id).one(db).await
}


// Get all CIDR bans in the database.
// TODO: pagination?
pub async fn fetch_all_cidr_bans(
db: &DbConn,
) -> Result<Vec<(cidr_ban::Model, Option<user::Model>)>, DbErr> {
Expand All @@ -75,6 +89,8 @@ impl Query {
.await
}

// Get all URL filters in the database.
// TODO: pagination?
pub async fn fetch_all_url_filters(
db: &DbConn,
) -> Result<Vec<(url_filter::Model, Option<user::Model>)>, DbErr> {
Expand All @@ -85,6 +101,7 @@ impl Query {
.await
}

// Check if an IP is banned or not.
pub async fn check_ip_ban(db: &DbConn, addr: IpAddr) -> Result<bool, DbErr> {
let octets: [u8; 16] = match addr {
IpAddr::V4(i) => i.to_ipv6_mapped(),
Expand Down
11 changes: 7 additions & 4 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@ use axum_login::{AuthUser, AuthnBackend, UserId};
use password_auth::verify_password;
use sea_orm::{DbConn, DbErr};
use serde::Deserialize;
use tokio::task;
use tokio::task::{spawn_blocking, JoinError};

use entity::user;
use service::Query;

// Various auth bits and pieces

#[derive(Debug, thiserror::Error)]
#[allow(clippy::module_name_repetitions)]
pub enum AuthError {
#[error(transparent)]
DbErr(#[from] DbErr),
Database(#[from] DbErr),

#[error(transparent)]
TaskJoin(#[from] task::JoinError),
TaskJoin(#[from] JoinError),
}

// We marshall the database user in and out of this
Expand Down Expand Up @@ -90,6 +92,7 @@ impl Backend {
}
}

// This struct also doubles as a form for our login page
#[derive(Debug, Clone, Deserialize)]
pub struct Credentials {
pub(crate) username: String,
Expand All @@ -109,7 +112,7 @@ impl AuthnBackend for Backend {
) -> Result<Option<Self::User>, Self::Error> {
let user = Self::User::find_user_by_username(&self.db, &creds.username).await?;

task::spawn_blocking(|| {
spawn_blocking(|| {
Ok(user.filter(|user| verify_password(creds.password, &user.0.password_hash).is_ok()))
})
.await?
Expand Down
6 changes: 6 additions & 0 deletions src/bancache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use tracing::trace;

use service::Query;

// This caches IP bans/allows so we don't hit the database so much.

const CACHE_ENTRIES: u64 = 10_000;

#[derive(Debug, thiserror::Error)]
Expand All @@ -39,6 +41,7 @@ pub struct BanCache {
impl BanCache {
pub(crate) fn new(db: Arc<DbConn>) -> Self {
Self {
// XXX - should these cache parameters be configurable?
cache: Cache::builder()
.max_capacity(CACHE_ENTRIES)
.time_to_live(Duration::days(1).unsigned_abs())
Expand All @@ -49,6 +52,7 @@ impl BanCache {
}
}

// Check for a ban, if it's not present, then check the database.
pub(crate) async fn check_ban(&self, ip: IpAddr) -> Result<bool, BanCacheError> {
if let Some(result) = self.cache.get(&ip).await {
trace!("{ip}: got a cache hit (banned: {result})");
Expand All @@ -61,6 +65,8 @@ impl BanCache {
}
}

// Invalidate a ban in the cache
// NOTE: this must be called after a ban is created *or* deleted.
pub(crate) fn invalidate(&self, network: IpNetwork) {
trace!("Invalidating cache for {network}");
self.cache
Expand Down
2 changes: 1 addition & 1 deletion src/csrf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::util::string::WebsafeAlphabet;
pub type CryptoEngine = Aes256GcmSiv;

const SESSION_KEY: &str = "shadyurl.csrf";
const MAX_DURATION: Duration = Duration::minutes(10); // FIXME - configurable?
const MAX_DURATION: Duration = Duration::minutes(10); // FIXME - configurable?

// Actual session data, a random token and the time the session began.
#[derive(Debug, Clone, Deserialize, Serialize)]
Expand Down
2 changes: 2 additions & 0 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ use serde::{
use tracing::error;
use validator::Validate;

// Routines to get configuration information from the environment and .env file

pub type Key = [u8; 32];

mod defaults {
Expand Down
5 changes: 5 additions & 0 deletions src/err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
*/

// Error generation and response stuff, used for handlers.

use askama_axum::Template;
use axum::{
body::Body,
Expand All @@ -27,6 +29,7 @@ use crate::{
util::net::{AddressError, NetworkPrefixError},
};

// Anything that can go wrong in a handler should go here.
#[derive(Debug, thiserror::Error)]
pub enum AppError {
#[error(transparent)]
Expand Down Expand Up @@ -79,6 +82,7 @@ impl IntoResponse for AppError {
Self::NotFound => ErrorResponse::not_found(),
Self::Unauthorized => ErrorResponse::unauthorized(),
_ => {
// If it's anything else, 500.
error!("Internal server error: {}", self.to_string());
ErrorResponse::internal_server_error(self.to_string().as_str())
}
Expand Down Expand Up @@ -113,6 +117,7 @@ struct UrlSubmissionErrorTemplate<'a> {
url: &'a str,
}

// This returns various canned responses for various issues.
pub struct ErrorResponse;

impl ErrorResponse {
Expand Down
Loading

0 comments on commit 1a4f3dd

Please sign in to comment.