diff options
Diffstat (limited to 'rust/src')
-rw-r--r-- | rust/src/auth.rs | 233 | ||||
-rw-r--r-- | rust/src/bin/fatcat-auth.rs | 2 | ||||
-rw-r--r-- | rust/src/bin/fatcat-doctor.rs | 96 | ||||
-rw-r--r-- | rust/src/bin/fatcat-export.rs | 14 | ||||
-rw-r--r-- | rust/src/bin/fatcatd.rs | 4 | ||||
-rw-r--r-- | rust/src/database_models.rs | 8 | ||||
-rw-r--r-- | rust/src/endpoint_handlers.rs | 10 | ||||
-rw-r--r-- | rust/src/entity_crud.rs | 38 | ||||
-rw-r--r-- | rust/src/errors.rs | 8 | ||||
-rw-r--r-- | rust/src/identifiers.rs | 4 |
10 files changed, 287 insertions, 130 deletions
diff --git a/rust/src/auth.rs b/rust/src/auth.rs index b88a72b0..3a6271e5 100644 --- a/rust/src/auth.rs +++ b/rust/src/auth.rs @@ -5,7 +5,7 @@ //! role-based authentication (RBAC). use data_encoding::BASE64; -use macaroon::{Format, Macaroon, Verifier}; +use macaroon::{ByteString, Caveat, Format, Macaroon, MacaroonError, MacaroonKey, Verifier}; use std::fmt; use swagger::auth::{AuthData, Authorization, Scopes}; @@ -23,10 +23,10 @@ use std::collections::HashMap; use std::env; use std::str::FromStr; -// 32 bytes max (!) -static DUMMY_KEY: &[u8] = b"dummy-key-a-one-two-three-a-la"; +// 32 bytes exactly +static DUMMY_KEY_BYTES: &[u8; 32] = b"dummy-key-a-one-two-three-a-la!!"; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum FatcatRole { Public, Editor, @@ -178,8 +178,23 @@ impl iron::middleware::BeforeMiddleware for MacaroonAuthMiddleware { pub struct AuthConfectionary { pub location: String, pub identifier: String, - pub key: Vec<u8>, - pub root_keys: HashMap<String, Vec<u8>>, + pub key: MacaroonKey, + pub root_keys: HashMap<String, MacaroonKey>, +} + +fn parse_macaroon_key(key_base64: &str) -> Result<MacaroonKey> { + // instead of creating a [u8; 32], we decode into an arbitrary Vec (after checking the input + // length first), because the MacaroonKey 'From' trait is implemented differently for [u8] and + // [u8; 32] (sigh). + if key_base64.len() != 44 { + bail!("bad base64-padded-encoded key for macaroons"); + } + let key_bytes = BASE64 + .decode(key_base64.as_bytes()) + .expect("base64 key decode"); + let bytes_ref: &[u8] = key_bytes.as_ref(); + let key = MacaroonKey::from(bytes_ref); + Ok(key) } impl AuthConfectionary { @@ -188,10 +203,10 @@ impl AuthConfectionary { identifier: String, key_base64: &str, ) -> Result<AuthConfectionary> { - macaroon::initialize().unwrap(); - let key = BASE64.decode(key_base64.as_bytes())?; + macaroon::initialize().expect("initializing macaroon library"); + let key = parse_macaroon_key(key_base64)?; let mut root_keys = HashMap::new(); - root_keys.insert(identifier.clone(), key.clone()); + root_keys.insert(identifier.clone(), key); Ok(AuthConfectionary { location, identifier, @@ -204,13 +219,13 @@ impl AuthConfectionary { AuthConfectionary::new( "test.fatcat.wiki".to_string(), "dummy".to_string(), - &BASE64.encode(DUMMY_KEY), + &BASE64.encode(DUMMY_KEY_BYTES), ) - .unwrap() + .expect("creating dummy AuthConfectionary") } pub fn add_keypair(&mut self, identifier: String, key_base64: &str) -> Result<()> { - let key = BASE64.decode(key_base64.as_bytes())?; + let key = parse_macaroon_key(key_base64)?; self.root_keys.insert(identifier, key); Ok(()) } @@ -220,23 +235,31 @@ impl AuthConfectionary { editor_id: FatcatId, duration: Option<chrono::Duration>, ) -> Result<String> { - let mut mac = Macaroon::create(&self.location, &self.key, &self.identifier) - .expect("Macaroon creation"); - mac.add_first_party_caveat(&format!("editor_id = {}", editor_id.to_string())); + let mut mac = Macaroon::create( + Some(self.location.clone()), + &self.key, + self.identifier.clone().into(), + ) + .expect("Macaroon creation"); + mac.add_first_party_caveat(format!("editor_id = {}", editor_id).into()); let now_utc = Utc::now(); let now = now_utc.to_rfc3339_opts(SecondsFormat::Secs, true); - mac.add_first_party_caveat(&format!("time > {}", now)); + mac.add_first_party_caveat(format!("time > {}", now).into()); if let Some(duration) = duration { let expires = now_utc + duration; - mac.add_first_party_caveat(&format!( - "time < {}", - &expires.to_rfc3339_opts(SecondsFormat::Secs, true) - )); + mac.add_first_party_caveat( + format!( + "time < {}", + &expires.to_rfc3339_opts(SecondsFormat::Secs, true) + ) + .into(), + ); }; let raw = mac.serialize(Format::V2).expect("macaroon serialization"); Ok(BASE64.encode(&raw)) } + /// Takes a macaroon as a base64-encoded string, deserializes it pub fn parse_macaroon_token( &self, conn: &DbConn, @@ -255,23 +278,19 @@ impl AuthConfectionary { .into()); } }; - let mac = match mac.validate() { - Ok(m) => m, - Err(e) => { - // TODO: should be "chaining" here - return Err(FatcatError::InvalidCredentials(format!( - "macaroon validate error: {:?}", - e - )) - .into()); - } - }; - let mut verifier = Verifier::new(); + let mut verifier = Verifier::default(); let mut editor_id: Option<FatcatId> = None; for caveat in mac.first_party_caveats() { - if caveat.predicate().starts_with("editor_id = ") { - editor_id = Some(FatcatId::from_str(caveat.predicate().get(12..).unwrap())?); - break; + if let Caveat::FirstParty(fp) = caveat { + let predicate_str = String::from_utf8(fp.predicate().as_ref().to_vec())?; + if predicate_str.starts_with("editor_id = ") { + editor_id = Some(FatcatId::from_str( + predicate_str + .get(12..) + .expect("parsing editor_id from macaroon"), + )?); + break; + } } } let editor_id = match editor_id { @@ -283,25 +302,28 @@ impl AuthConfectionary { .into()); } }; - verifier.satisfy_exact(&format!("editor_id = {}", editor_id.to_string())); + verifier.satisfy_exact(format!("editor_id = {}", editor_id).into()); if let Some(endpoint) = endpoint { // API endpoint - verifier.satisfy_exact(&format!("endpoint = {}", endpoint)); + verifier.satisfy_exact(format!("endpoint = {}", endpoint).into()); } let mut created: Option<DateTime<Utc>> = None; for caveat in mac.first_party_caveats() { - if caveat.predicate().starts_with("time > ") { - let ts: chrono::ParseResult<DateTime<Utc>> = - DateTime::parse_from_rfc3339(caveat.predicate().get(7..).unwrap()) - .map(|x| x.with_timezone(&Utc)); - if let Ok(ts) = ts { - created = Some(ts); - break; - } else { - info!( - "couldn't parse macaroon time constraint: {}", - caveat.predicate() - ); + if let Caveat::FirstParty(fp) = caveat { + let predicate_str = String::from_utf8(fp.predicate().as_ref().to_vec())?; + if predicate_str.starts_with("time > ") { + let ts: chrono::ParseResult<DateTime<Utc>> = DateTime::parse_from_rfc3339( + predicate_str + .get(7..) + .expect("parsing timestamp from macaroon"), + ) + .map(|x| x.with_timezone(&Utc)); + if let Ok(ts) = ts { + created = Some(ts); + break; + } else { + info!("couldn't parse macaroon time constraint: {}", predicate_str); + } } } } @@ -314,23 +336,25 @@ impl AuthConfectionary { .into()); } }; - verifier.satisfy_exact(&format!( - "time > {}", - created.to_rfc3339_opts(SecondsFormat::Secs, true) - )); + verifier.satisfy_exact( + format!( + "time > {}", + created.to_rfc3339_opts(SecondsFormat::Secs, true) + ) + .into(), + ); // not finding the editor_id is an auth issue, not a 404 - let editor: EditorRow = - match Editor::db_get(conn, editor_id).map_err(|e| FatcatError::from(e)) { - Ok(ed) => ed, - Err(FatcatError::NotFound(_, _)) => { - return Err(FatcatError::InvalidCredentials(format!( - "editor_id not found: {}", - editor_id - )) - .into()); - } - other_db_err => other_db_err?, - }; + let editor: EditorRow = match Editor::db_get(conn, editor_id).map_err(FatcatError::from) { + Ok(ed) => ed, + Err(FatcatError::NotFound(_, _)) => { + return Err(FatcatError::InvalidCredentials(format!( + "editor_id not found: {}", + editor_id + )) + .into()); + } + other_db_err => other_db_err?, + }; let auth_epoch = DateTime::<Utc>::from_utc(editor.auth_epoch, Utc); // allow a second of wiggle room for precision and, eg, tests if created < (auth_epoch - chrono::Duration::seconds(1)) { @@ -340,39 +364,48 @@ impl AuthConfectionary { ) .into()); } - verifier.satisfy_general(|p: &str| -> bool { - // not expired (based on time) - if p.starts_with("time < ") { - let expires: chrono::ParseResult<DateTime<Utc>> = - DateTime::parse_from_rfc3339(p.get(7..).unwrap()) - .map(|x| x.with_timezone(&Utc)); - if let Ok(when) = expires { - //info!("checking time constraint: {} < {}", Utc::now(), when); - Utc::now() < when + verifier.satisfy_general(|p_bstr: &ByteString| -> bool { + if let Ok(p) = String::from_utf8(p_bstr.as_ref().to_vec()) { + // not expired (based on time) + if p.starts_with("time < ") { + let expires: chrono::ParseResult<DateTime<Utc>> = DateTime::parse_from_rfc3339( + p.get(7..).expect("parsing datetime from macaroon"), + ) + .map(|x| x.with_timezone(&Utc)); + if let Ok(when) = expires { + //info!("checking time constraint: {} < {}", Utc::now(), when); + Utc::now() < when + } else { + info!("couldn't parse macaroon time constraint: {}", p); + false + } } else { - info!("couldn't parse macaroon time constraint: {}", p); false } } else { false } }); - let verify_key = match self.root_keys.get(mac.identifier()) { + let verify_key = match self + .root_keys + .get(std::str::from_utf8(mac.identifier().as_ref())?) + { Some(key) => key, None => { return Err(FatcatError::InvalidCredentials(format!( "no valid auth signing key for identifier: {}", - mac.identifier() + std::str::from_utf8(mac.identifier().as_ref())? )) .into()); } }; - match mac.verify(verify_key, &mut verifier) { - Ok(true) => (), - Ok(false) => { - return Err(FatcatError::InvalidCredentials( - "auth token (macaroon) not valid (signature and/or caveats failed)".to_string(), - ) + match verifier.verify(&mac, verify_key, Default::default()) { + Ok(()) => (), + Err(MacaroonError::InvalidMacaroon(em)) => { + return Err(FatcatError::InvalidCredentials(format!( + "auth token (macaroon) not valid (signature and/or caveats failed): {}", + em + )) .into()); } Err(e) => { @@ -448,9 +481,15 @@ impl AuthConfectionary { let now = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true); println!("current time: {}", now); println!("domain (location): {:?}", mac.location()); - println!("signing key name (identifier): {}", mac.identifier()); + println!( + "signing key name (identifier): {}", + std::str::from_utf8(mac.identifier().as_ref())? + ); for caveat in mac.first_party_caveats() { - println!("caveat: {}", caveat.predicate()); + if let Caveat::FirstParty(fp) = caveat { + let predicate_str = String::from_utf8(fp.predicate().as_ref().to_vec())?; + println!("caveat: {}", predicate_str); + } } println!("verify: {:?}", self.parse_macaroon_token(conn, token, None)); Ok(()) @@ -488,7 +527,7 @@ pub fn print_editors(conn: &DbConn) -> Result<()> { for e in all_editors { println!( "{}\t{}/{}/{}\t{}\t{}\t{:?}", - FatcatId::from_uuid(&e.id).to_string(), + FatcatId::from_uuid(&e.id), e.is_superuser, e.is_admin, e.is_bot, @@ -519,3 +558,25 @@ pub fn env_confectionary() -> Result<AuthConfectionary> { }; Ok(confectionary) } + +#[test] +fn test_macaroon_keys() { + assert!(parse_macaroon_key("blah").is_err()); + + let key_bytes: [u8; 32] = [ + 231, 158, 121, 231, 158, 121, 231, 158, 121, 231, 158, 121, 231, 158, 121, 231, 158, 121, + 231, 158, 121, 231, 158, 121, 231, 158, 121, 231, 158, 121, 198, 107, + ]; + + // old way of parsing keys + let old_key = BASE64 + .decode(b"5555555555555555555555555555555555555555xms=") + .unwrap(); + assert_eq!(old_key.len(), 32); + assert_eq!(old_key, key_bytes); + let old_macaroon_key: MacaroonKey = old_key.as_slice().into(); + + // new (2022) way of parsing keys + let key = parse_macaroon_key("5555555555555555555555555555555555555555xms=").unwrap(); + assert_eq!(old_macaroon_key, key); +} diff --git a/rust/src/bin/fatcat-auth.rs b/rust/src/bin/fatcat-auth.rs index 0b5c05b0..c351848e 100644 --- a/rust/src/bin/fatcat-auth.rs +++ b/rust/src/bin/fatcat-auth.rs @@ -89,7 +89,7 @@ fn main() -> Result<()> { }; let editor_row = editor.db_create(&db_conn)?; //println!("{:?}", editor); - println!("{}", FatcatId::from_uuid(&editor_row.id).to_string()); + println!("{}", FatcatId::from_uuid(&editor_row.id)); } ("create-token", Some(subm)) => { let editor_id = FatcatId::from_str(subm.value_of("editor-id").unwrap())?; diff --git a/rust/src/bin/fatcat-doctor.rs b/rust/src/bin/fatcat-doctor.rs new file mode 100644 index 00000000..7255a22f --- /dev/null +++ b/rust/src/bin/fatcat-doctor.rs @@ -0,0 +1,96 @@ +//! Database cleanup tool + +use clap::{value_t_or_exit, App, SubCommand}; + +use fatcat::database_models::*; +use fatcat::database_schema::*; +use fatcat::errors::Result; +use fatcat::identifiers::FatcatId; +use fatcat::server; +use fatcat::server::DbConn; +use std::process; +use std::str::FromStr; + +use diesel::prelude::*; + +fn backfill_changelog_gap(conn: &DbConn, last_good: i64, max_index: i64) -> Result<()> { + // sanity check arguments against database + assert!(last_good > 0); + assert!(max_index > 0); + assert!(last_good < max_index); + let highest_row: ChangelogRow = changelog::table.order(changelog::id.desc()).first(conn)?; + assert!(highest_row.id >= max_index); + + // default values + // 'root' editor_id is aaaaaaaaaaaabkvkaaaaaaaaae + // 'admin' editor_id is aaaaaaaaaaaabkvkaaaaaaaaai + let editor_id = FatcatId::from_str("aaaaaaaaaaaabkvkaaaaaaaaai").unwrap(); + let description = "Backfill of missing changelog entries due to database id gap"; + + // fetch the last entry before the gap, to re-use the timestamp + let existing_row: ChangelogRow = changelog::table.find(last_good).first(conn)?; + + for index in last_good + 1..max_index + 1 { + // ensure this index is actually a gap + let count: i64 = changelog::table + .filter(changelog::id.eq(index)) + .count() + .get_result(conn)?; + if count != 0 { + println!("Found existing changelog: {}", index); + return Ok(()); + } + + // create dummy empty editgroup, then add a changelog entry + let eg_row: EditgroupRow = diesel::insert_into(editgroup::table) + .values(( + editgroup::editor_id.eq(editor_id.to_uuid()), + editgroup::created.eq(existing_row.timestamp), + editgroup::is_accepted.eq(true), + editgroup::description.eq(Some(description)), + )) + .get_result(conn)?; + let _entry_row: ChangelogRow = diesel::insert_into(changelog::table) + .values(( + changelog::id.eq(index), + changelog::editgroup_id.eq(eg_row.id), + changelog::timestamp.eq(existing_row.timestamp), + )) + .get_result(conn)?; + println!("Inserted changelog: {}", index); + } + Ok(()) +} + +fn main() -> Result<()> { + let m = App::new("fatcat-doctor") + .version(env!("CARGO_PKG_VERSION")) + .author("Bryan Newbold <bnewbold@archive.org>") + .about("Database cleanup / fixup tool") + .subcommand( + SubCommand::with_name("backfill-changelog-gap") + .about("Inserts dummy changelog entries and editgroups for gap") + .args_from_usage( + "<start> 'changelog index of entry just before gap' + <end> 'highest changelog index to backfill'", + ), + ) + .get_matches(); + + let db_conn = server::database_worker_pool()? + .get() + .expect("database pool"); + match m.subcommand() { + ("backfill-changelog-gap", Some(subm)) => { + let last_good: i64 = value_t_or_exit!(subm.value_of("start"), i64); + let max_index: i64 = value_t_or_exit!(subm.value_of("end"), i64); + backfill_changelog_gap(&db_conn, last_good, max_index)?; + } + _ => { + println!("Missing or unimplemented command!"); + println!("{}", m.usage()); + process::exit(-1); + } + } + Ok(()) +} diff --git a/rust/src/bin/fatcat-export.rs b/rust/src/bin/fatcat-export.rs index 7d671b9a..9ac977aa 100644 --- a/rust/src/bin/fatcat-export.rs +++ b/rust/src/bin/fatcat-export.rs @@ -28,7 +28,7 @@ use std::thread; const CHANNEL_BUFFER_LEN: usize = 200; arg_enum! { - #[derive(PartialEq, Debug, Clone, Copy)] + #[derive(PartialEq, Debug, Clone, Copy, Eq)] pub enum ExportEntityType { Creator, Container, @@ -163,7 +163,7 @@ fn parse_line(s: &str) -> Result<IdentRow> { let group_id: Option<FatcatId> = if fields.len() == 4 { match fields[3].as_ref() { "" => None, - val => Some(FatcatId::from_uuid(&Uuid::from_str(&val)?)), + val => Some(FatcatId::from_uuid(&Uuid::from_str(val)?)), } } else if fields.len() == 3 { None @@ -174,11 +174,11 @@ fn parse_line(s: &str) -> Result<IdentRow> { ident_id: FatcatId::from_uuid(&Uuid::from_str(&fields[0])?), rev_id: match fields[1].as_ref() { "" => None, - val => Some(Uuid::from_str(&val)?), + val => Some(Uuid::from_str(val)?), }, redirect_id: match fields[2].as_ref() { "" => None, - val => Some(FatcatId::from_uuid(&Uuid::from_str(&val)?)), + val => Some(FatcatId::from_uuid(&Uuid::from_str(val)?)), }, group_id, }) @@ -331,7 +331,7 @@ pub fn do_export_batch( (Some(_), Some(_), false) => (), _ => { if row.group_id == None || row.group_id != last_group_id { - if batch.len() > 0 { + if !batch.is_empty() { row_sender.send(batch); batch = vec![]; } @@ -345,7 +345,7 @@ pub fn do_export_batch( info!("processed {} lines...", count); } } - if batch.len() > 0 { + if !batch.is_empty() { row_sender.send(batch); } drop(row_sender); @@ -385,7 +385,7 @@ fn main() -> Result<()> { None => std::cmp::min(1, num_cpus::get() / 2) as usize, }; let expand = match m.value_of("expand") { - Some(s) => Some(ExpandFlags::from_str(&s)?), + Some(s) => Some(ExpandFlags::from_str(s)?), None => None, }; let log_level = if m.is_present("quiet") { diff --git a/rust/src/bin/fatcatd.rs b/rust/src/bin/fatcatd.rs index 006154df..bfa41805 100644 --- a/rust/src/bin/fatcatd.rs +++ b/rust/src/bin/fatcatd.rs @@ -27,7 +27,7 @@ pub struct XClacksOverheadMiddleware; impl AfterMiddleware for XClacksOverheadMiddleware { fn after(&self, _req: &mut Request, mut res: Response) -> iron::IronResult<Response> { res.headers - .set(XClacksOverhead("GNU aaronsw, jpb".to_owned())); + .set(XClacksOverhead("GNU aaronsw, jpb, pde".to_owned())); Ok(res) } } @@ -48,7 +48,7 @@ fn main() -> Result<()> { let drain = slog_async::Async::new(drain).build().fuse(); let logger = Logger::root(drain, o!()); let _scope_guard = slog_scope::set_global_logger(logger.clone()); - let _log_guard = slog_stdlog::init().unwrap(); + slog_stdlog::init().unwrap(); let formatter = DefaultLogFormatter; // sentry exception handling diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs index 0427f9c8..b3b64126 100644 --- a/rust/src/database_models.rs +++ b/rust/src/database_models.rs @@ -13,7 +13,7 @@ use uuid::Uuid; // Ugh. I thought the whole point was to *not* do this, but: // https://github.com/diesel-rs/diesel/issues/1589 -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum EntityState { WorkInProgress, Active(Uuid), @@ -552,7 +552,7 @@ pub struct RefsBlobRow { pub refs_json: serde_json::Value, } -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq)] /// This model is a stable representation of what goes in a RefsBlobRow `refs_json` field (an array /// of this model). We could rely on the `ReleaseRef` API spec model directly, but that would lock /// the database contents to the API spec rigidly; by defining this struct independently, we can @@ -684,8 +684,8 @@ impl EditgroupRow { Editgroup { editgroup_id: Some(uuid2fcid(&self.id)), editor_id: Some(uuid2fcid(&self.editor_id)), - editor: editor, - changelog_index: changelog_index, + editor, + changelog_index, created: Some(chrono::DateTime::from_utc(self.created, chrono::Utc)), submitted: self .submitted diff --git a/rust/src/endpoint_handlers.rs b/rust/src/endpoint_handlers.rs index 9a76652b..ed6f6641 100644 --- a/rust/src/endpoint_handlers.rs +++ b/rust/src/endpoint_handlers.rs @@ -171,7 +171,7 @@ impl Server { }; let mut entity = ContainerEntity::db_from_row(conn, rev, Some(ident), hide_flags)?; - entity.db_expand(&conn, expand_flags)?; + entity.db_expand(conn, expand_flags)?; Ok(entity) } @@ -210,7 +210,7 @@ impl Server { }; let mut entity = CreatorEntity::db_from_row(conn, rev, Some(ident), hide_flags)?; - entity.db_expand(&conn, expand_flags)?; + entity.db_expand(conn, expand_flags)?; Ok(entity) } @@ -280,7 +280,7 @@ impl Server { }; let mut entity = FileEntity::db_from_row(conn, rev, Some(ident), hide_flags)?; - entity.db_expand(&conn, expand_flags)?; + entity.db_expand(conn, expand_flags)?; Ok(entity) } @@ -705,7 +705,7 @@ impl Server { }; let mut entity = ReleaseEntity::db_from_row(conn, rev, Some(ident), hide_flags)?; - entity.db_expand(&conn, expand_flags)?; + entity.db_expand(conn, expand_flags)?; Ok(entity) } @@ -906,7 +906,7 @@ impl Server { username.truncate(24); let editor = Editor { editor_id: None, - username: username, + username, is_admin: Some(false), is_bot: Some(false), is_active: Some(true), diff --git a/rust/src/entity_crud.rs b/rust/src/entity_crud.rs index e8f63d0c..6dc9ff18 100644 --- a/rust/src/entity_crud.rs +++ b/rust/src/entity_crud.rs @@ -96,7 +96,7 @@ where fn db_insert_revs(conn: &DbConn, models: &[&Self]) -> Result<Vec<Uuid>>; } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct ExpandFlags { pub files: bool, pub filesets: bool, @@ -187,7 +187,7 @@ fn test_expand_flags() { ); } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct HideFlags { // release pub abstracts: bool, @@ -1195,7 +1195,7 @@ impl EntityCrud for FileEntity { None => (), Some(url_list) => { let these_url_rows: Vec<FileRevUrlNewRow> = url_list - .into_iter() + .iter() .map(|u| FileRevUrlNewRow { file_rev: *rev_id, rel: u.rel.clone(), @@ -1399,7 +1399,7 @@ impl EntityCrud for FilesetEntity { None => (), Some(file_list) => { let these_file_rows: Vec<FilesetRevFileNewRow> = file_list - .into_iter() + .iter() .map(|f| FilesetRevFileNewRow { fileset_rev: *rev_id, path_name: f.path.clone(), @@ -1419,7 +1419,7 @@ impl EntityCrud for FilesetEntity { None => (), Some(url_list) => { let these_url_rows: Vec<FilesetRevUrlNewRow> = url_list - .into_iter() + .iter() .map(|u| FilesetRevUrlNewRow { fileset_rev: *rev_id, rel: u.rel.clone(), @@ -1654,11 +1654,11 @@ impl EntityCrud for WebcaptureEntity { None => (), Some(cdx_list) => { let these_cdx_rows: Vec<WebcaptureRevCdxNewRow> = cdx_list - .into_iter() + .iter() .map(|c| WebcaptureRevCdxNewRow { webcapture_rev: *rev_id, surt: c.surt.clone(), - timestamp: c.timestamp.clone(), + timestamp: c.timestamp, url: c.url.clone(), mimetype: c.mimetype.clone(), status_code: c.status_code, @@ -1675,7 +1675,7 @@ impl EntityCrud for WebcaptureEntity { None => (), Some(url_list) => { let these_url_rows: Vec<WebcaptureRevUrlNewRow> = url_list - .into_iter() + .iter() .map(|u| WebcaptureRevUrlNewRow { webcapture_rev: *rev_id, rel: u.rel.clone(), @@ -1818,8 +1818,8 @@ impl EntityCrud for ReleaseEntity { Some(ident) => match &self.redirect { // If we're a redirect, then expand for the *target* identifier, not *our* // identifier. Tricky! - None => FatcatId::from_str(&ident)?, - Some(redir) => FatcatId::from_str(&redir)?, + None => FatcatId::from_str(ident)?, + Some(redir) => FatcatId::from_str(redir)?, }, }; self.files = Some(get_release_files(conn, ident, HideFlags::none())?); @@ -1828,8 +1828,8 @@ impl EntityCrud for ReleaseEntity { let ident = match &self.ident { None => bail!("Can't expand filesets on a non-concrete entity"), // redundant with above is_some() Some(ident) => match &self.redirect { - None => FatcatId::from_str(&ident)?, - Some(redir) => FatcatId::from_str(&redir)?, + None => FatcatId::from_str(ident)?, + Some(redir) => FatcatId::from_str(redir)?, }, }; self.filesets = Some(get_release_filesets(conn, ident, HideFlags::none())?); @@ -1838,8 +1838,8 @@ impl EntityCrud for ReleaseEntity { let ident = match &self.ident { None => bail!("Can't expand webcaptures on a non-concrete entity"), // redundant with above is_some() Some(ident) => match &self.redirect { - None => FatcatId::from_str(&ident)?, - Some(redir) => FatcatId::from_str(&redir)?, + None => FatcatId::from_str(ident)?, + Some(redir) => FatcatId::from_str(redir)?, }, }; self.webcaptures = Some(get_release_webcaptures(conn, ident, HideFlags::none())?); @@ -1848,7 +1848,7 @@ impl EntityCrud for ReleaseEntity { if let Some(ref cid) = self.container_id { self.container = Some(ContainerEntity::db_get( conn, - FatcatId::from_str(&cid)?, + FatcatId::from_str(cid)?, HideFlags::none(), )?); } @@ -1929,7 +1929,7 @@ impl EntityCrud for ReleaseEntity { model }) .collect(); - let model_refs: Vec<&Self> = models_with_work_ids.iter().map(|s| s).collect(); + let model_refs: Vec<&Self> = models_with_work_ids.iter().collect(); let models = model_refs.as_slice(); // The rest here is copy/pasta from the generic (how to avoid copypasta?) @@ -1952,7 +1952,7 @@ impl EntityCrud for ReleaseEntity { rev_ids .into_iter() .zip(ident_ids.into_iter()) - .zip(models.into_iter().map(|m| m.edit_extra.clone())) + .zip(models.iter().map(|m| m.edit_extra.clone())) .map(|((rev_id, ident_id), edit_extra)| Self::EditNewRow { editgroup_id: edit_context.editgroup_id.to_uuid(), rev_id: Some(rev_id), @@ -2101,7 +2101,7 @@ impl EntityCrud for ReleaseEntity { withdrawn_status: rev_row.withdrawn_status, withdrawn_date: rev_row.withdrawn_date, withdrawn_year: rev_row.withdrawn_year, - ext_ids: ext_ids, + ext_ids, volume: rev_row.volume, issue: rev_row.issue, pages: rev_row.pages, @@ -2460,7 +2460,7 @@ impl EntityCrud for ReleaseEntity { .collect(); abstract_rows.extend(new_abstracts); let new_release_abstract_rows: Vec<ReleaseRevAbstractNewRow> = abstract_list - .into_iter() + .iter() .map(|c| { Ok(ReleaseRevAbstractNewRow { release_rev: *rev_id, diff --git a/rust/src/errors.rs b/rust/src/errors.rs index ea0f9646..38429802 100644 --- a/rust/src/errors.rs +++ b/rust/src/errors.rs @@ -173,16 +173,16 @@ impl From<data_encoding::DecodeError> for FatcatError { impl From<failure::Error> for FatcatError { fn from(error: failure::Error) -> FatcatError { // TODO: I think it should be possible to match here? regardless, this is *super* janky - if let Some(_) = error.downcast_ref::<FatcatError>() { + if error.downcast_ref::<FatcatError>().is_some() { return error.downcast::<FatcatError>().unwrap(); } - if let Some(_) = error.downcast_ref::<std::fmt::Error>() { + if error.downcast_ref::<std::fmt::Error>().is_some() { return error.downcast::<std::fmt::Error>().unwrap().into(); } - if let Some(_) = error.downcast_ref::<diesel::result::Error>() { + if error.downcast_ref::<diesel::result::Error>().is_some() { return error.downcast::<diesel::result::Error>().unwrap().into(); } - if let Some(_) = error.downcast_ref::<uuid::ParseError>() { + if error.downcast_ref::<uuid::ParseError>().is_some() { return error.downcast::<uuid::ParseError>().unwrap().into(); } FatcatError::InternalError(error.to_string()) diff --git a/rust/src/identifiers.rs b/rust/src/identifiers.rs index e9baf7b8..56174be1 100644 --- a/rust/src/identifiers.rs +++ b/rust/src/identifiers.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use std::{convert, fmt}; use uuid::Uuid; -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Debug, Eq)] pub struct FatcatId(Uuid); impl fmt::Display for FatcatId { @@ -51,7 +51,7 @@ impl FatcatId { /// Convert fatcat IDs (base32 strings) to UUID pub fn fcid2uuid(fcid: &str) -> Result<Uuid> { - if fcid.is_ascii() == false || fcid.len() != 26 { + if !fcid.is_ascii() || fcid.len() != 26 { return Err(FatcatError::InvalidFatcatId(fcid.to_string()).into()); } let mut raw = vec![0; 16]; |