Skip to content

Commit 4559b6b

Browse files
authored
Merge pull request #11222 from Turbo87/trustpub-cleanup
Add `trustpub::DeleteExpiredJtis/Tokens` background jobs
2 parents 1b93ce9 + 829fcad commit 4559b6b

File tree

9 files changed

+204
-0
lines changed

9 files changed

+204
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
mod github_config;
2+
mod token;
3+
mod used_jti;
24

35
pub use self::github_config::{GitHubConfig, NewGitHubConfig};
6+
pub use self::token::NewToken;
7+
pub use self::used_jti::NewUsedJti;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use crate::schema::trustpub_tokens;
2+
use chrono::{DateTime, Utc};
3+
use diesel::prelude::*;
4+
use diesel_async::{AsyncPgConnection, RunQueryDsl};
5+
6+
#[derive(Debug, Insertable)]
7+
#[diesel(table_name = trustpub_tokens, check_for_backend(diesel::pg::Pg))]
8+
pub struct NewToken<'a> {
9+
pub expires_at: DateTime<Utc>,
10+
pub hashed_token: &'a [u8],
11+
pub crate_ids: &'a [i32],
12+
}
13+
14+
impl NewToken<'_> {
15+
pub async fn insert(&self, conn: &mut AsyncPgConnection) -> QueryResult<()> {
16+
self.insert_into(trustpub_tokens::table)
17+
.execute(conn)
18+
.await?;
19+
20+
Ok(())
21+
}
22+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use crate::schema::trustpub_used_jtis;
2+
use chrono::{DateTime, Utc};
3+
use diesel::prelude::*;
4+
use diesel_async::{AsyncPgConnection, RunQueryDsl};
5+
6+
#[derive(Debug, Insertable)]
7+
#[diesel(table_name = trustpub_used_jtis, check_for_backend(diesel::pg::Pg))]
8+
pub struct NewUsedJti<'a> {
9+
pub jti: &'a str,
10+
pub expires_at: DateTime<Utc>,
11+
}
12+
13+
impl<'a> NewUsedJti<'a> {
14+
pub fn new(jti: &'a str, expires_at: DateTime<Utc>) -> Self {
15+
Self { jti, expires_at }
16+
}
17+
18+
pub async fn insert(&self, conn: &mut AsyncPgConnection) -> QueryResult<usize> {
19+
diesel::insert_into(trustpub_used_jtis::table)
20+
.values(self)
21+
.execute(conn)
22+
.await
23+
}
24+
}

src/bin/crates-admin/enqueue_job.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub enum Command {
4949
name: String,
5050
},
5151
SyncUpdatesFeed,
52+
TrustpubCleanup,
5253
}
5354

5455
pub async fn run(command: Command) -> Result<()> {
@@ -161,6 +162,13 @@ pub async fn run(command: Command) -> Result<()> {
161162
Command::SyncUpdatesFeed => {
162163
jobs::rss::SyncUpdatesFeed.enqueue(&mut conn).await?;
163164
}
165+
Command::TrustpubCleanup => {
166+
let job = jobs::trustpub::DeleteExpiredTokens;
167+
job.enqueue(&mut conn).await?;
168+
169+
let job = jobs::trustpub::DeleteExpiredJtis;
170+
job.enqueue(&mut conn).await?;
171+
}
164172
};
165173

166174
Ok(())

src/worker/jobs/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod readmes;
1212
pub mod rss;
1313
mod send_publish_notifications;
1414
mod sync_admins;
15+
pub mod trustpub;
1516
mod typosquat;
1617
mod update_default_version;
1718

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use crate::worker::Environment;
2+
use crates_io_database::schema::trustpub_used_jtis;
3+
use crates_io_worker::BackgroundJob;
4+
use diesel::prelude::*;
5+
use diesel_async::RunQueryDsl;
6+
use std::sync::Arc;
7+
8+
/// A background job that deletes expired JSON Web Token IDs (JTIs)
9+
/// tokens from the database.
10+
#[derive(Deserialize, Serialize)]
11+
pub struct DeleteExpiredJtis;
12+
13+
impl BackgroundJob for DeleteExpiredJtis {
14+
const JOB_NAME: &'static str = "trustpub::delete_expired_jtis";
15+
16+
type Context = Arc<Environment>;
17+
18+
async fn run(&self, ctx: Self::Context) -> anyhow::Result<()> {
19+
let mut conn = ctx.deadpool.get().await?;
20+
21+
diesel::delete(trustpub_used_jtis::table)
22+
.filter(trustpub_used_jtis::expires_at.lt(diesel::dsl::now))
23+
.execute(&mut conn)
24+
.await?;
25+
26+
Ok(())
27+
}
28+
}
29+
30+
#[cfg(test)]
31+
mod tests {
32+
use super::*;
33+
use crate::tests::util::TestApp;
34+
use chrono::{TimeDelta, Utc};
35+
use crates_io_database::models::trustpub::NewUsedJti;
36+
use insta::assert_compact_debug_snapshot;
37+
38+
#[tokio::test(flavor = "multi_thread")]
39+
async fn test_expiry() -> anyhow::Result<()> {
40+
let (app, _client) = TestApp::full().empty().await;
41+
let mut conn = app.db_conn().await;
42+
43+
let jti = NewUsedJti {
44+
expires_at: Utc::now() + TimeDelta::minutes(30),
45+
jti: "foo",
46+
};
47+
jti.insert(&mut conn).await?;
48+
49+
let jti = NewUsedJti {
50+
expires_at: Utc::now() - TimeDelta::minutes(5),
51+
jti: "bar",
52+
};
53+
jti.insert(&mut conn).await?;
54+
55+
DeleteExpiredJtis.enqueue(&mut conn).await?;
56+
app.run_pending_background_jobs().await;
57+
58+
// Check that the expired token was deleted
59+
let known_jtis: Vec<String> = trustpub_used_jtis::table
60+
.select(trustpub_used_jtis::jti)
61+
.load(&mut conn)
62+
.await?;
63+
64+
assert_compact_debug_snapshot!(known_jtis, @r#"["foo"]"#);
65+
66+
Ok(())
67+
}
68+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use crate::worker::Environment;
2+
use crates_io_database::schema::trustpub_tokens;
3+
use crates_io_worker::BackgroundJob;
4+
use diesel::prelude::*;
5+
use diesel_async::RunQueryDsl;
6+
use std::sync::Arc;
7+
8+
/// A background job that deletes expired temporary access
9+
/// tokens from the database.
10+
#[derive(Deserialize, Serialize)]
11+
pub struct DeleteExpiredTokens;
12+
13+
impl BackgroundJob for DeleteExpiredTokens {
14+
const JOB_NAME: &'static str = "trustpub::delete_expired_tokens";
15+
16+
type Context = Arc<Environment>;
17+
18+
async fn run(&self, ctx: Self::Context) -> anyhow::Result<()> {
19+
let mut conn = ctx.deadpool.get().await?;
20+
21+
diesel::delete(trustpub_tokens::table)
22+
.filter(trustpub_tokens::expires_at.lt(diesel::dsl::now))
23+
.execute(&mut conn)
24+
.await?;
25+
26+
Ok(())
27+
}
28+
}
29+
30+
#[cfg(test)]
31+
mod tests {
32+
use super::*;
33+
use crate::tests::util::TestApp;
34+
use chrono::{TimeDelta, Utc};
35+
use crates_io_database::models::trustpub::NewToken;
36+
use insta::assert_compact_debug_snapshot;
37+
38+
#[tokio::test(flavor = "multi_thread")]
39+
async fn test_expiry() -> anyhow::Result<()> {
40+
let (app, _client) = TestApp::full().empty().await;
41+
let mut conn = app.db_conn().await;
42+
43+
let token = NewToken {
44+
expires_at: Utc::now() + TimeDelta::minutes(30),
45+
hashed_token: &[0xC0, 0xFF, 0xEE],
46+
crate_ids: &[1],
47+
};
48+
token.insert(&mut conn).await?;
49+
50+
let token = NewToken {
51+
expires_at: Utc::now() - TimeDelta::minutes(5),
52+
hashed_token: &[0xBA, 0xAD, 0xF0, 0x0D],
53+
crate_ids: &[2],
54+
};
55+
token.insert(&mut conn).await?;
56+
57+
DeleteExpiredTokens.enqueue(&mut conn).await?;
58+
app.run_pending_background_jobs().await;
59+
60+
// Check that the expired token was deleted
61+
let crate_ids: Vec<Vec<Option<i32>>> = trustpub_tokens::table
62+
.select(trustpub_tokens::crate_ids)
63+
.load(&mut conn)
64+
.await?;
65+
66+
assert_compact_debug_snapshot!(crate_ids, @"[[Some(1)]]");
67+
68+
Ok(())
69+
}
70+
}

src/worker/jobs/trustpub/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod delete_jtis;
2+
mod delete_tokens;
3+
4+
pub use delete_jtis::DeleteExpiredJtis;
5+
pub use delete_tokens::DeleteExpiredTokens;

src/worker/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@ impl RunnerExt for Runner<Arc<Environment>> {
4343
.register_job_type::<jobs::rss::SyncCrateFeed>()
4444
.register_job_type::<jobs::rss::SyncCratesFeed>()
4545
.register_job_type::<jobs::rss::SyncUpdatesFeed>()
46+
.register_job_type::<jobs::trustpub::DeleteExpiredJtis>()
47+
.register_job_type::<jobs::trustpub::DeleteExpiredTokens>()
4648
}
4749
}

0 commit comments

Comments
 (0)