Compare commits

...

3 Commits

Author SHA1 Message Date
Jef Roosens fd1c2d3647
feat(server): configurable api key
ci/woodpecker/push/lint Pipeline was successful Details
ci/woodpecker/push/clippy Pipeline failed Details
ci/woodpecker/push/build Pipeline was successful Details
2023-08-03 11:08:38 +02:00
Jef Roosens 33c8477b09
feat(server): log repository updates 2023-08-03 11:03:01 +02:00
Jef Roosens a7e0c03b58
feat(server): authorized requests 2023-08-03 09:34:33 +02:00
6 changed files with 47 additions and 10 deletions

View File

@ -16,3 +16,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
requests
* Packages of architecture "any" are part of every architecture's
database
* Bearer authentication for private routes

13
Cargo.lock generated
View File

@ -243,6 +243,12 @@ dependencies = [
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "base64"
version = "0.21.2"
@ -1762,7 +1768,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2"
dependencies = [
"base64",
"base64 0.21.2",
]
[[package]]
@ -2255,7 +2261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ca69bf415b93b60b80dc8fda3cb4ef52b2336614d8da2de5456cc942a110482"
dependencies = [
"atoi",
"base64",
"base64 0.21.2",
"bigdecimal",
"bitflags 2.3.3",
"byteorder",
@ -2302,7 +2308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0db2df1b8731c3651e204629dd55e52adbae0462fa1bdcbed56a2302c18181e"
dependencies = [
"atoi",
"base64",
"base64 0.21.2",
"bigdecimal",
"bitflags 2.3.3",
"byteorder",
@ -2616,6 +2622,7 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c"
dependencies = [
"base64 0.20.0",
"bitflags 2.3.3",
"bytes",
"futures-core",

View File

@ -18,7 +18,7 @@ sha256 = "1.1.4"
tokio = { version = "1.29.1", features = ["full"] }
tokio-util = { version = "0.7.8", features = ["io"] }
tower = { version = "0.4.13", features = ["make"] }
tower-http = { version = "0.4.1", features = ["fs", "trace"] }
tower-http = { version = "0.4.1", features = ["fs", "trace", "auth"] }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
uuid = { version = "1.4.0", features = ["v4"] }

View File

@ -18,6 +18,8 @@ pub struct Cli {
pub pkg_dir: PathBuf,
/// Directory where repository metadata & SQLite database is stored
pub data_dir: PathBuf,
/// API key to authenticate private routes with
pub api_key: String,
/// Database connection URL; either sqlite:// or postgres://. Defaults to rieter.sqlite in the
/// data directory
@ -72,6 +74,7 @@ impl Cli {
data_dir: self.data_dir.clone(),
repo_dir: self.data_dir.join("repos"),
pkg_dir: self.pkg_dir.clone(),
api_key: self.api_key.clone(),
};
let repo_manager = RepoGroupManager::new(&config.repo_dir, &self.pkg_dir);
@ -84,7 +87,7 @@ impl Cli {
// build our application with a single route
let app = Router::new()
.nest("/api", crate::api::router())
.merge(crate::repo::router())
.merge(crate::repo::router(&self.api_key))
.with_state(global)
.layer(TraceLayer::new_for_http());

View File

@ -16,6 +16,7 @@ pub struct Config {
data_dir: PathBuf,
repo_dir: PathBuf,
pkg_dir: PathBuf,
api_key: String,
}
#[derive(Clone)]

View File

@ -17,15 +17,28 @@ use std::sync::Arc;
use tokio::{fs, io::AsyncWriteExt};
use tower::util::ServiceExt;
use tower_http::services::{ServeDir, ServeFile};
use tower_http::validate_request::ValidateRequestHeaderLayer;
use uuid::Uuid;
pub fn router() -> Router<crate::Global> {
pub fn router(api_key: &str) -> Router<crate::Global> {
Router::new()
.route("/:repo", post(post_package_archive).delete(delete_repo))
.route("/:repo/:arch", delete(delete_arch_repo))
.route(
"/:repo",
post(post_package_archive)
.delete(delete_repo)
.route_layer(ValidateRequestHeaderLayer::bearer(api_key)),
)
.route(
"/:repo/:arch",
delete(delete_arch_repo).route_layer(ValidateRequestHeaderLayer::bearer(api_key)),
)
// Routes added after the layer do not get that layer applied, so the GET requests will not
// be authorized
.route(
"/:repo/:arch/:filename",
delete(delete_package).get(get_file),
delete(delete_package)
.route_layer(ValidateRequestHeaderLayer::bearer(api_key))
.get(get_file),
)
}
@ -57,6 +70,8 @@ async fn post_package_archive(
match res {
// Insert the newly added package into the database
Ok(pkg) => {
tracing::info!("Added '{}' to repository '{}'", pkg.file_name(), repo);
// Query the repo for its ID, or create it if it does not already exist
let repo_entity = db_repo::Entity::find()
.filter(db_repo::Column::Name.eq(&repo))
@ -150,10 +165,14 @@ async fn delete_repo(
) -> crate::Result<StatusCode> {
let clone = Arc::clone(&global.repo_manager);
let repo_clone = repo.clone();
let repo_removed =
tokio::task::spawn_blocking(move || clone.write().unwrap().remove_repo(&repo)).await??;
tokio::task::spawn_blocking(move || clone.write().unwrap().remove_repo(&repo_clone))
.await??;
if repo_removed {
tracing::info!("Removed repository '{}'", repo);
Ok(StatusCode::OK)
} else {
Ok(StatusCode::NOT_FOUND)
@ -166,11 +185,14 @@ async fn delete_arch_repo(
) -> crate::Result<StatusCode> {
let clone = Arc::clone(&global.repo_manager);
let log = format!("Removed architecture '{}' from repository '{}'", arch, repo);
let repo_removed =
tokio::task::spawn_blocking(move || clone.write().unwrap().remove_repo_arch(&repo, &arch))
.await??;
if repo_removed {
tracing::info!(log);
Ok(StatusCode::OK)
} else {
Ok(StatusCode::NOT_FOUND)
@ -190,6 +212,7 @@ async fn delete_package(
}
let name = name_parts[..name_parts.len() - 3].join("-");
let log = format!("Removed '{}' from repository '{}'", file_name, repo);
let clone = Arc::clone(&global.repo_manager);
@ -199,6 +222,8 @@ async fn delete_package(
.await??;
if pkg_removed {
tracing::info!(log);
Ok(StatusCode::OK)
} else {
Ok(StatusCode::NOT_FOUND)