diff --git a/server/src/access/http.rs b/server/src/access/http.rs index e91ccc0..e292961 100644 --- a/server/src/access/http.rs +++ b/server/src/access/http.rs @@ -34,6 +34,13 @@ impl AuthState { } } + /// Returns the username if it exists. + /// + /// Currently it's the `sub` claim of the JWT. + pub fn username(&self) -> Option<&str> { + self.token.get().map(|token| token.sub()) + } + /// Finds and performs authorization for a cache. pub async fn auth_cache( &self, diff --git a/server/src/access/mod.rs b/server/src/access/mod.rs index b579b6e..0db3da0 100644 --- a/server/src/access/mod.rs +++ b/server/src/access/mod.rs @@ -232,6 +232,11 @@ impl Token { .map_err(|e| Error::TokenError(e).into()) } + /// Returns the subject of the token. + pub fn sub(&self) -> &str { + self.0.claims.sub.as_str() + } + /// Returns the claims as a serializable value. pub fn opaque_claims(&self) -> &impl Serialize { &self.0.claims diff --git a/server/src/api/v1/upload_path.rs b/server/src/api/v1/upload_path.rs index eb0c01b..61f7a73 100644 --- a/server/src/api/v1/upload_path.rs +++ b/server/src/api/v1/upload_path.rs @@ -107,22 +107,26 @@ pub(crate) async fn upload_path( stream.map(|r| r.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))), ); + let username = req_state.auth.username() + .map(str::to_string); + // Try to acquire a lock on an existing NAR let existing_nar = database.find_and_lock_nar(&upload_info.nar_hash).await?; match existing_nar { Some(existing_nar) => { // Deduplicate - upload_path_dedup(cache, upload_info, stream, existing_nar, database).await + upload_path_dedup(username, cache, upload_info, stream, existing_nar, database).await } None => { // New NAR - upload_path_new(cache, upload_info, stream, database, &state).await + upload_path_new(username, cache, upload_info, stream, database, &state).await } } } /// Uploads a path when there is already a matching NAR in the global cache. async fn upload_path_dedup( + username: Option, cache: cache::Model, upload_info: UploadPathNarInfo, stream: impl AsyncRead + Unpin, @@ -164,6 +168,7 @@ async fn upload_path_dedup( new_object.cache_id = Set(cache.id); new_object.nar_id = Set(existing_nar.id); new_object.created_at = Set(Utc::now()); + new_object.created_by = Set(username); new_object }) .exec(&txn) @@ -185,6 +190,7 @@ async fn upload_path_dedup( /// us. The `nar` table can hold duplicate NARs which can be deduplicated /// in a background process. async fn upload_path_new( + username: Option, cache: cache::Model, upload_info: UploadPathNarInfo, stream: impl AsyncRead + Send + Unpin + 'static, @@ -308,6 +314,7 @@ async fn upload_path_new( new_object.cache_id = Set(cache.id); new_object.nar_id = Set(nar_id); new_object.created_at = Set(Utc::now()); + new_object.created_by = Set(username); new_object }) .exec(&txn) diff --git a/server/src/database/entity/object.rs b/server/src/database/entity/object.rs index ec36fcc..da6233f 100644 --- a/server/src/database/entity/object.rs +++ b/server/src/database/entity/object.rs @@ -62,6 +62,12 @@ pub struct Model { /// Timestamp when the object is last accessed. pub last_accessed_at: Option, + + /// The uploader of the object. + /// + /// This is a "username." Currently, it's set to the `sub` claim in + /// the client's JWT. + pub created_by: Option, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/server/src/database/migration/m20230103_000001_add_object_created_by.rs b/server/src/database/migration/m20230103_000001_add_object_created_by.rs new file mode 100644 index 0000000..8dcfe93 --- /dev/null +++ b/server/src/database/migration/m20230103_000001_add_object_created_by.rs @@ -0,0 +1,27 @@ +use sea_orm_migration::prelude::*; + +use crate::database::entity::object::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20230103_000001_add_object_created_by" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .alter_table( + Table::alter() + .table(Entity) + .add_column(ColumnDef::new(Column::CreatedBy).string().null()) + .to_owned(), + ) + .await?; + + Ok(()) + } +} diff --git a/server/src/database/migration/mod.rs b/server/src/database/migration/mod.rs index 2146e3d..b42033f 100644 --- a/server/src/database/migration/mod.rs +++ b/server/src/database/migration/mod.rs @@ -7,6 +7,7 @@ mod m20221227_000002_create_nar_table; mod m20221227_000003_create_object_table; mod m20221227_000004_add_object_last_accessed; mod m20221227_000005_add_cache_retention_period; +mod m20230103_000001_add_object_created_by; pub struct Migrator; @@ -19,6 +20,7 @@ impl MigratorTrait for Migrator { Box::new(m20221227_000003_create_object_table::Migration), Box::new(m20221227_000004_add_object_last_accessed::Migration), Box::new(m20221227_000005_add_cache_retention_period::Migration), + Box::new(m20230103_000001_add_object_created_by::Migration), ] } }