From b5c770b349d283c9d979a49a0635cd46d06e201c Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Sat, 27 May 2023 00:56:48 -0400 Subject: [PATCH] create auth check helper function --- src/api/blobs.rs | 13 ++++++------- src/api/manifests.rs | 14 +++----------- src/auth_storage.rs | 38 +++++++++++++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/api/blobs.rs b/src/api/blobs.rs index a19760d..e2444ee 100644 --- a/src/api/blobs.rs +++ b/src/api/blobs.rs @@ -8,7 +8,7 @@ use axum::response::{IntoResponse, Response}; use tokio_util::io::ReaderStream; use crate::app_state::AppState; -use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response}; +use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response, does_user_have_repository_permission}; use crate::database::Database; use crate::dto::RepositoryVisibility; use crate::dto::user::{Permission, RegistryUserType, UserAuth}; @@ -16,11 +16,14 @@ use crate::dto::user::{Permission, RegistryUserType, UserAuth}; pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String)>, state: State>, Extension(auth): Extension) -> Response { // Check if the user has permission to pull, or that the repository is public let database = &state.database; - if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap() + /* if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap() && !database.get_repository_visibility(&name).await.unwrap() .and_then(|v| Some(v == RepositoryVisibility::Public)) .unwrap_or_else(|| false) { + return get_unauthenticated_response(&state.config); + } */ + if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() { return get_unauthenticated_response(&state.config); } drop(database); @@ -45,11 +48,7 @@ pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String pub async fn pull_digest_get(Path((name, layer_digest)): Path<(String, String)>, state: State>, Extension(auth): Extension) -> Response { // Check if the user has permission to pull, or that the repository is public let database = &state.database; - if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap() - && !database.get_repository_visibility(&name).await.unwrap() - .and_then(|v| Some(v == RepositoryVisibility::Public)) - .unwrap_or_else(|| false) { - + if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() { return get_unauthenticated_response(&state.config); } drop(database); diff --git a/src/api/manifests.rs b/src/api/manifests.rs index 46d71a8..22b15aa 100644 --- a/src/api/manifests.rs +++ b/src/api/manifests.rs @@ -7,7 +7,7 @@ use axum::http::{StatusCode, HeaderMap, HeaderName, header}; use tracing::log::warn; use tracing::{debug, info}; -use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response}; +use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response, does_user_have_repository_permission}; use crate::app_state::AppState; use crate::database::Database; use crate::dto::RepositoryVisibility; @@ -64,11 +64,7 @@ pub async fn upload_manifest_put(Path((name, reference)): Path<(String, String)> pub async fn pull_manifest_get(Path((name, reference)): Path<(String, String)>, state: State>, Extension(auth): Extension) -> Response { // Check if the user has permission to pull, or that the repository is public let database = &state.database; - if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap() - && !database.get_repository_visibility(&name).await.unwrap() - .and_then(|v| Some(v == RepositoryVisibility::Public)) - .unwrap_or_else(|| false) { - + if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() { return get_unauthenticated_response(&state.config); } drop(database); @@ -111,11 +107,7 @@ pub async fn pull_manifest_get(Path((name, reference)): Path<(String, String)>, pub async fn manifest_exists_head(Path((name, reference)): Path<(String, String)>, state: State>, Extension(auth): Extension) -> Response { // Check if the user has permission to pull, or that the repository is public let database = &state.database; - if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap() - && !database.get_repository_visibility(&name).await.unwrap() - .and_then(|v| Some(v == RepositoryVisibility::Public)) - .unwrap_or_else(|| false) { - + if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() { return get_unauthenticated_response(&state.config); } drop(database); diff --git a/src/auth_storage.rs b/src/auth_storage.rs index 964d010..6db6f14 100644 --- a/src/auth_storage.rs +++ b/src/auth_storage.rs @@ -4,7 +4,7 @@ use axum::{extract::{State, Path}, http::{StatusCode, HeaderMap, header, HeaderN use tracing::debug; -use crate::{app_state::AppState, dto::user::{Permission, RegistryUserType}, config::Config}; +use crate::{app_state::AppState, dto::{user::{Permission, RegistryUserType}, RepositoryVisibility}, config::Config}; use crate::database::Database; /// Temporary struct for storing auth information in memory. @@ -73,14 +73,42 @@ pub async fn require_auth(State(state): State>, mut request: Re } } -pub async fn does_user_have_permission(database: &impl Database, username: String, repository: String, permission: Permission) -> sqlx::Result { +pub async fn does_user_have_permission(database: &impl Database, username: String, repository: String, permission: Permission) -> anyhow::Result { + does_user_have_repository_permission(database, username, repository, permission, None).await +} + +/// Checks if a user has permission to do something in a repository. +/// +/// * `database`: Database connection. +/// * `username`: Name of the user. +/// * `repository`: Name of the repository. +/// * `permissions`: Permission to check for. +/// * `required_visibility`: Specified if there is a specific visibility of the repository that will give the user permission. +pub async fn does_user_have_repository_permission(database: &impl Database, username: String, repository: String, permission: Permission, required_visibility: Option) -> anyhow::Result { let allowed_to = { - match database.get_user_registry_type(username.clone()).await.unwrap() { + match database.get_user_registry_type(username.clone()).await? { Some(RegistryUserType::Admin) => true, - _ => match database.get_user_repo_permissions(username, repository).await.unwrap() { + _ => { + if let Some(perms) = database.get_user_repo_permissions(username, repository.clone()).await? { + if perms.has_permission(permission) { + return Ok(true); + } + } + + if let Some(vis) = required_visibility { + if let Some(repo_vis) = database.get_repository_visibility(&repository).await? { + if vis == repo_vis { + return Ok(true); + } + } + } + + false + } + /* match database.get_user_repo_permissions(username, repository).await.unwrap() { Some(perms) => if perms.has_permission(permission) { true } else { false }, _ => false, - } + } */ } };