Don't allow user to download a crate over the API if they don't have the VISIBLE permission :tada:
Diff
chartered-db/src/users.rs | 25 ++++++++++++++++++++++++-
chartered-web/src/middleware/auth.rs | 2 +-
chartered-web/src/endpoints/cargo_api/download.rs | 18 ++++++++++++++++++
3 files changed, 41 insertions(+), 4 deletions(-)
@@ -80,7 +80,7 @@
}
bitflags::bitflags! {
#[derive(FromSqlRow, AsExpression)]
#[derive(FromSqlRow, AsExpression, Default)]
pub struct UserCratePermissionValue: i32 {
const VISIBLE = 0b0000_0000_0000_0000_0000_0000_0000_0001;
const PUBLISH_VERSION = 0b0000_0000_0000_0000_0000_0000_0000_0010;
@@ -103,13 +103,34 @@
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Eq, Hash, Debug)]
#[derive(Identifiable, Queryable, Associations, Default, PartialEq, Eq, Hash, Debug)]
#[belongs_to(User)]
pub struct UserCratePermission {
pub id: i32,
pub user_id: i32,
pub crate_id: i32,
pub permissions: UserCratePermissionValue,
}
impl UserCratePermission {
pub async fn find(
conn: ConnectionPool,
given_user_id: i32,
given_crate_id: i32,
) -> Result<Option<UserCratePermission>> {
use crate::schema::user_crate_permissions::dsl::{crate_id, user_id};
tokio::task::spawn_blocking(move || {
let conn = conn.get()?;
Ok(crate::schema::user_crate_permissions::table
.filter(user_id.eq(given_user_id))
.filter(crate_id.eq(given_crate_id))
.get_result(&conn)
.optional()?)
})
.await?
}
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Eq, Hash, Debug)]
@@ -54,7 +54,7 @@
.await
.unwrap()
{
Some(user) => user,
Some(user) => std::sync::Arc::new(user),
None => {
return Ok(Response::builder()
.status(StatusCode::UNAUTHORIZED)
@@ -1,5 +1,9 @@
use axum::extract;
use chartered_db::{crates::Crate, ConnectionPool};
use chartered_db::{
crates::Crate,
users::{User, UserCratePermission, UserCratePermissionValue},
ConnectionPool,
};
use chartered_fs::FileSystem;
use std::{str::FromStr, sync::Arc};
@@ -13,10 +17,22 @@
pub async fn handle(
extract::Path((_api_key, name, version)): extract::Path<(String, String, String)>,
extract::Extension(db): extract::Extension<ConnectionPool>,
extract::Extension(user): extract::Extension<Arc<User>>,
) -> Result<Vec<u8>, Error> {
let c = Crate::find_by_name(db.clone(), name)
.await?
.ok_or(Error::NoCrate)?;
let perms = UserCratePermission::find(db.clone(), user.id, c.id)
.await?
.unwrap_or_default();
if !perms
.permissions
.contains(UserCratePermissionValue::VISIBLE)
{
return Err(Error::NoCrate);
}
let version = Arc::new(c)
.version(db, version)