🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-09-07 16:30:54.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-09-07 16:30:54.0 +01:00:00
commit
f2c016701c7cd6803ae87e3e7e41a7b6f0e9d33c [patch]
tree
c690312bf8afa9f874610b96a877e326613eeebe
parent
998db8626d668c412e6d1cbc2cbc633e4418e10d
download
f2c016701c7cd6803ae87e3e7e41a7b6f0e9d33c.tar.gz

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(-)

diff --git a/chartered-db/src/users.rs b/chartered-db/src/users.rs
index 5be434e..905ae65 100644
--- a/chartered-db/src/users.rs
+++ a/chartered-db/src/users.rs
@@ -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)]
diff --git a/chartered-web/src/middleware/auth.rs b/chartered-web/src/middleware/auth.rs
index af2112d..c3a53c8 100644
--- a/chartered-web/src/middleware/auth.rs
+++ a/chartered-web/src/middleware/auth.rs
@@ -54,7 +54,7 @@
                .await
                .unwrap()
            {
                Some(user) => user,
                Some(user) => std::sync::Arc::new(user),
                None => {
                    return Ok(Response::builder()
                        .status(StatusCode::UNAUTHORIZED)
diff --git a/chartered-web/src/endpoints/cargo_api/download.rs b/chartered-web/src/endpoints/cargo_api/download.rs
index 828a864..7f4e64a 100644
--- a/chartered-web/src/endpoints/cargo_api/download.rs
+++ a/chartered-web/src/endpoints/cargo_api/download.rs
@@ -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)