create AuthDriver trait for checking user permissions
This commit is contained in:
parent
b5c770b349
commit
d98a8e3790
|
@ -15,6 +15,8 @@ use rand::Rng;
|
||||||
use crate::{dto::{scope::Scope, user::{UserAuth, TokenInfo}}, app_state::AppState};
|
use crate::{dto::{scope::Scope, user::{UserAuth, TokenInfo}}, app_state::AppState};
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
|
|
||||||
|
use crate::auth_storage::{unauthenticated_response, AuthDriver};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct TokenAuthRequest {
|
pub struct TokenAuthRequest {
|
||||||
user: Option<String>,
|
user: Option<String>,
|
||||||
|
@ -165,13 +167,14 @@ pub async fn auth_basic_get(basic_auth: Option<AuthBasic>, state: State<Arc<AppS
|
||||||
|
|
||||||
if let (Some(account), Some(password)) = (&auth.account, auth.password) {
|
if let (Some(account), Some(password)) = (&auth.account, auth.password) {
|
||||||
// Ensure that the password is correct
|
// Ensure that the password is correct
|
||||||
let database = &state.database;
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
if !database.verify_user_login(account.clone(), password).await.unwrap() {
|
if !auth_driver.verify_user_login(account.clone(), password).await.unwrap() {
|
||||||
debug!("Authentication failed, incorrect password!");
|
debug!("Authentication failed, incorrect password!");
|
||||||
return (
|
|
||||||
StatusCode::UNAUTHORIZED
|
return unauthenticated_response(&state.config);
|
||||||
).into_response();
|
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
debug!("User password is correct");
|
debug!("User password is correct");
|
||||||
|
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
|
@ -193,6 +196,7 @@ pub async fn auth_basic_get(basic_auth: Option<AuthBasic>, state: State<Arc<AppS
|
||||||
|
|
||||||
let json_str = serde_json::to_string(&auth_response).unwrap();
|
let json_str = serde_json::to_string(&auth_response).unwrap();
|
||||||
|
|
||||||
|
let database = &state.database;
|
||||||
database.store_user_token(token_str.clone(), account.clone(), token.expiry, token.created_at).await.unwrap();
|
database.store_user_token(token_str.clone(), account.clone(), token.expiry, token.created_at).await.unwrap();
|
||||||
drop(database);
|
drop(database);
|
||||||
|
|
||||||
|
|
|
@ -8,25 +8,18 @@ use axum::response::{IntoResponse, Response};
|
||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response, does_user_have_repository_permission};
|
use crate::auth_storage::{unauthenticated_response, AuthDriver};
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::dto::RepositoryVisibility;
|
use crate::dto::RepositoryVisibility;
|
||||||
use crate::dto::user::{Permission, RegistryUserType, UserAuth};
|
use crate::dto::user::{Permission, RegistryUserType, UserAuth};
|
||||||
|
|
||||||
pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn digest_exists_head(Path((name, layer_digest)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
// Check if the user has permission to pull, or that the repository is public
|
// Check if the user has permission to pull, or that the repository is public
|
||||||
let database = &state.database;
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
/* if !does_user_have_permission(database, auth.user.username, name.clone(), Permission::PULL).await.unwrap()
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
||||||
&& !database.get_repository_visibility(&name).await.unwrap()
|
return unauthenticated_response(&state.config);
|
||||||
.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);
|
drop(auth_driver);
|
||||||
|
|
||||||
let storage = state.storage.lock().await;
|
let storage = state.storage.lock().await;
|
||||||
|
|
||||||
|
@ -47,11 +40,11 @@ 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<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn pull_digest_get(Path((name, layer_digest)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
// Check if the user has permission to pull, or that the repository is public
|
// Check if the user has permission to pull, or that the repository is public
|
||||||
let database = &state.database;
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
||||||
return get_unauthenticated_response(&state.config);
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
drop(database);
|
drop(auth_driver);
|
||||||
|
|
||||||
let storage = state.storage.lock().await;
|
let storage = state.storage.lock().await;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use axum::http::{StatusCode, HeaderMap, HeaderName, header};
|
||||||
use tracing::log::warn;
|
use tracing::log::warn;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response, does_user_have_repository_permission};
|
use crate::auth_storage::{unauthenticated_response, AuthDriver};
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::dto::RepositoryVisibility;
|
use crate::dto::RepositoryVisibility;
|
||||||
|
@ -16,9 +16,11 @@ use crate::dto::manifest::Manifest;
|
||||||
use crate::dto::user::{UserAuth, Permission};
|
use crate::dto::user::{UserAuth, Permission};
|
||||||
|
|
||||||
pub async fn upload_manifest_put(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>, body: String) -> Response {
|
pub async fn upload_manifest_put(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>, body: String) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
// Calculate the sha256 digest for the manifest.
|
// Calculate the sha256 digest for the manifest.
|
||||||
let calculated_hash = sha256::digest(body.clone());
|
let calculated_hash = sha256::digest(body.clone());
|
||||||
|
@ -63,11 +65,11 @@ 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<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn pull_manifest_get(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
// Check if the user has permission to pull, or that the repository is public
|
// Check if the user has permission to pull, or that the repository is public
|
||||||
let database = &state.database;
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
||||||
return get_unauthenticated_response(&state.config);
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
drop(database);
|
drop(auth_driver);
|
||||||
|
|
||||||
let database = &state.database;
|
let database = &state.database;
|
||||||
let digest = match Digest::is_digest(&reference) {
|
let digest = match Digest::is_digest(&reference) {
|
||||||
|
@ -106,11 +108,11 @@ 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<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn manifest_exists_head(Path((name, reference)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
// Check if the user has permission to pull, or that the repository is public
|
// Check if the user has permission to pull, or that the repository is public
|
||||||
let database = &state.database;
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
if !does_user_have_repository_permission(database, auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PULL, Some(RepositoryVisibility::Public)).await.unwrap() {
|
||||||
return get_unauthenticated_response(&state.config);
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
drop(database);
|
drop(auth_driver);
|
||||||
|
|
||||||
// Get the digest from the reference path.
|
// Get the digest from the reference path.
|
||||||
let database = &state.database;
|
let database = &state.database;
|
||||||
|
@ -146,9 +148,11 @@ pub async fn manifest_exists_head(Path((name, reference)): Path<(String, String)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete_manifest(Path((name, reference)): Path<(String, String)>, headers: HeaderMap, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn delete_manifest(Path((name, reference)): Path<(String, String)>, headers: HeaderMap, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
let _authorization = headers.get("Authorization").unwrap(); // TODO: use authorization header
|
let _authorization = headers.get("Authorization").unwrap(); // TODO: use authorization header
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,15 @@ use futures::StreamExt;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::app_state::AppState;
|
use crate::app_state::AppState;
|
||||||
use crate::auth_storage::{does_user_have_permission, get_unauthenticated_response};
|
use crate::auth_storage::{unauthenticated_response, AuthDriver};
|
||||||
use crate::byte_stream::ByteStream;
|
use crate::byte_stream::ByteStream;
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::dto::user::{UserAuth, Permission, RegistryUser, RegistryUserType};
|
use crate::dto::user::{UserAuth, Permission, RegistryUser, RegistryUserType};
|
||||||
|
|
||||||
/// Starting an upload
|
/// Starting an upload
|
||||||
pub async fn start_upload_post(Path((name, )): Path<(String, )>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>) -> Response {
|
pub async fn start_upload_post(Path((name, )): Path<(String, )>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>) -> Response {
|
||||||
if does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
|
if auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
debug!("Upload requested");
|
debug!("Upload requested");
|
||||||
let uuid = uuid::Uuid::new_v4();
|
let uuid = uuid::Uuid::new_v4();
|
||||||
|
|
||||||
|
@ -34,13 +35,15 @@ pub async fn start_upload_post(Path((name, )): Path<(String, )>, Extension(auth)
|
||||||
).into_response();
|
).into_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
get_unauthenticated_response(&state.config)
|
unauthenticated_response(&state.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn chunked_upload_layer_patch(Path((name, layer_uuid)): Path<(String, String)>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, mut body: BodyStream) -> Response {
|
pub async fn chunked_upload_layer_patch(Path((name, layer_uuid)): Path<(String, String)>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, mut body: BodyStream) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
let storage = state.storage.lock().await;
|
let storage = state.storage.lock().await;
|
||||||
let current_size = storage.digest_length(&layer_uuid).await.unwrap();
|
let current_size = storage.digest_length(&layer_uuid).await.unwrap();
|
||||||
|
@ -95,9 +98,11 @@ pub async fn chunked_upload_layer_patch(Path((name, layer_uuid)): Path<(String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn finish_chunked_upload_put(Path((name, layer_uuid)): Path<(String, String)>, Query(query): Query<HashMap<String, String>>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, body: Bytes) -> Response {
|
pub async fn finish_chunked_upload_put(Path((name, layer_uuid)): Path<(String, String)>, Query(query): Query<HashMap<String, String>>, Extension(auth): Extension<UserAuth>, state: State<Arc<AppState>>, body: Bytes) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
let digest = query.get("digest").unwrap();
|
let digest = query.get("digest").unwrap();
|
||||||
|
|
||||||
|
@ -122,9 +127,11 @@ pub async fn finish_chunked_upload_put(Path((name, layer_uuid)): Path<(String, S
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn cancel_upload_delete(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn cancel_upload_delete(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
let storage = state.storage.lock().await;
|
let storage = state.storage.lock().await;
|
||||||
storage.delete_digest(&layer_uuid).await.unwrap();
|
storage.delete_digest(&layer_uuid).await.unwrap();
|
||||||
|
@ -134,9 +141,11 @@ pub async fn cancel_upload_delete(Path((name, layer_uuid)): Path<(String, String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn check_upload_status_get(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
pub async fn check_upload_status_get(Path((name, layer_uuid)): Path<(String, String)>, state: State<Arc<AppState>>, Extension(auth): Extension<UserAuth>) -> Response {
|
||||||
if !does_user_have_permission(&state.database, auth.user.username, name.clone(), Permission::PUSH).await.unwrap() {
|
let auth_driver = state.auth_checker.lock().await;
|
||||||
return get_unauthenticated_response(&state.config);
|
if !auth_driver.user_has_permission(auth.user.username, name.clone(), Permission::PUSH, None).await.unwrap() {
|
||||||
|
return unauthenticated_response(&state.config);
|
||||||
}
|
}
|
||||||
|
drop(auth_driver);
|
||||||
|
|
||||||
let storage = state.storage.lock().await;
|
let storage = state.storage.lock().await;
|
||||||
let ending = storage.digest_length(&layer_uuid).await.unwrap().unwrap_or(0);
|
let ending = storage.digest_length(&layer_uuid).await.unwrap().unwrap_or(0);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use sqlx::{Sqlite, Pool};
|
use sqlx::{Sqlite, Pool};
|
||||||
|
|
||||||
use crate::auth_storage::MemoryAuthStorage;
|
use crate::auth_storage::AuthDriver;
|
||||||
use crate::storage::StorageDriver;
|
use crate::storage::StorageDriver;
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
|
@ -10,17 +10,17 @@ pub struct AppState {
|
||||||
pub database: Pool<Sqlite>,
|
pub database: Pool<Sqlite>,
|
||||||
pub storage: Mutex<Box<dyn StorageDriver>>,
|
pub storage: Mutex<Box<dyn StorageDriver>>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub auth_storage: Mutex<MemoryAuthStorage>,
|
pub auth_checker: Mutex<Box<dyn AuthDriver>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
pub fn new(database: Pool<Sqlite>, storage: Mutex<Box<dyn StorageDriver>>, config: Config) -> Self
|
pub fn new(database: Pool<Sqlite>, storage: Mutex<Box<dyn StorageDriver>>, config: Config, auth_checker: Mutex<Box<dyn AuthDriver>>) -> Self
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
database,
|
database,
|
||||||
storage,
|
storage,
|
||||||
config,
|
config,
|
||||||
auth_storage: Mutex::new(MemoryAuthStorage::new()),
|
auth_checker,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,13 +2,66 @@ use std::{collections::HashSet, ops::Deref, sync::Arc};
|
||||||
|
|
||||||
use axum::{extract::{State, Path}, http::{StatusCode, HeaderMap, header, HeaderName, Request}, middleware::Next, response::{Response, IntoResponse}};
|
use axum::{extract::{State, Path}, http::{StatusCode, HeaderMap, header, HeaderName, Request}, middleware::Next, response::{Response, IntoResponse}};
|
||||||
|
|
||||||
|
use sqlx::{Pool, Sqlite};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{app_state::AppState, dto::{user::{Permission, RegistryUserType}, RepositoryVisibility}, config::Config};
|
use crate::{app_state::AppState, dto::{user::{Permission, RegistryUserType}, RepositoryVisibility}, config::Config};
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AuthDriver: Send + Sync {
|
||||||
|
/// Checks if a user has permission to do something in a repository.
|
||||||
|
///
|
||||||
|
/// * `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.
|
||||||
|
async fn user_has_permission(&self, username: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool>;
|
||||||
|
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AuthDriver for Pool<Sqlite> {
|
||||||
|
async fn user_has_permission(&self, username: String, repository: String, permission: Permission, required_visibility: Option<RepositoryVisibility>) -> anyhow::Result<bool> {
|
||||||
|
let allowed_to = {
|
||||||
|
match self.get_user_registry_type(username.clone()).await? {
|
||||||
|
Some(RegistryUserType::Admin) => true,
|
||||||
|
_ => {
|
||||||
|
if let Some(perms) = self.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) = self.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,
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(allowed_to)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn verify_user_login(&self, username: String, password: String) -> anyhow::Result<bool> {
|
||||||
|
Database::verify_user_login(self, username, password).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Temporary struct for storing auth information in memory.
|
/// Temporary struct for storing auth information in memory.
|
||||||
pub struct MemoryAuthStorage {
|
/* pub struct MemoryAuthStorage {
|
||||||
pub valid_tokens: HashSet<String>,
|
pub valid_tokens: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +71,7 @@ impl MemoryAuthStorage {
|
||||||
valid_tokens: HashSet::new(),
|
valid_tokens: HashSet::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} */
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AuthToken(pub String);
|
pub struct AuthToken(pub String);
|
||||||
|
@ -73,49 +126,8 @@ pub async fn require_auth<B>(State(state): State<Arc<AppState>>, mut request: Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn does_user_have_permission(database: &impl Database, username: String, repository: String, permission: Permission) -> anyhow::Result<bool> {
|
#[inline(always)]
|
||||||
does_user_have_repository_permission(database, username, repository, permission, None).await
|
pub fn unauthenticated_response(config: &Config) -> Response {
|
||||||
}
|
|
||||||
|
|
||||||
/// 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<RepositoryVisibility>) -> anyhow::Result<bool> {
|
|
||||||
let allowed_to = {
|
|
||||||
match database.get_user_registry_type(username.clone()).await? {
|
|
||||||
Some(RegistryUserType::Admin) => true,
|
|
||||||
_ => {
|
|
||||||
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,
|
|
||||||
} */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(allowed_to)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_unauthenticated_response(config: &Config) -> Response {
|
|
||||||
let bearer = format!("Bearer realm=\"{}/auth\"", config.get_url());
|
let bearer = format!("Bearer realm=\"{}/auth\"", config.get_url());
|
||||||
(
|
(
|
||||||
StatusCode::UNAUTHORIZED,
|
StatusCode::UNAUTHORIZED,
|
||||||
|
|
|
@ -63,3 +63,6 @@ CREATE TABLE IF NOT EXISTS user_tokens (
|
||||||
expiry BIGINT NOT NULL,
|
expiry BIGINT NOT NULL,
|
||||||
created_at BIGINT NOT NULL
|
created_at BIGINT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- create admin user
|
||||||
|
INSERT OR IGNORE INTO users (username, email, password_hash, password_salt) VALUES ('admin', 'admin@example.com', '$2b$12$x5ECk0jUmOSfBWxW52wsyOmFxNZkwc2J9FH225if4eBnQYUvYLYYq', 'x5ECk0jUmOSfBWxW52wsyO');
|
|
@ -12,6 +12,7 @@ use std::net::SocketAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use auth_storage::AuthDriver;
|
||||||
use axum::http::{Request, StatusCode, header, HeaderName};
|
use axum::http::{Request, StatusCode, header, HeaderName};
|
||||||
use axum::middleware::Next;
|
use axum::middleware::Next;
|
||||||
use axum::response::{Response, IntoResponse};
|
use axum::response::{Response, IntoResponse};
|
||||||
|
@ -66,11 +67,12 @@ async fn main() -> std::io::Result<()> {
|
||||||
pool.create_schema().await.unwrap();
|
pool.create_schema().await.unwrap();
|
||||||
|
|
||||||
let storage_driver: Mutex<Box<dyn StorageDriver>> = Mutex::new(Box::new(FilesystemDriver::new("registry/blobs")));
|
let storage_driver: Mutex<Box<dyn StorageDriver>> = Mutex::new(Box::new(FilesystemDriver::new("registry/blobs")));
|
||||||
|
let auth_driver: Mutex<Box<dyn AuthDriver>> = Mutex::new(Box::new(pool.clone()));
|
||||||
|
|
||||||
let config = Config::new().expect("Failure to parse config!");
|
let config = Config::new().expect("Failure to parse config!");
|
||||||
let app_addr = SocketAddr::from_str(&format!("{}:{}", config.listen_address, config.listen_port)).unwrap();
|
let app_addr = SocketAddr::from_str(&format!("{}:{}", config.listen_address, config.listen_port)).unwrap();
|
||||||
|
|
||||||
let state = Arc::new(AppState::new(pool, storage_driver, config));
|
let state = Arc::new(AppState::new(pool, storage_driver, config, auth_driver));
|
||||||
|
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_max_level(Level::DEBUG)
|
.with_max_level(Level::DEBUG)
|
||||||
|
|
Loading…
Reference in New Issue