diff --git a/crates/app/src/auth/mod.rs b/crates/app/src/auth/mod.rs
index 4b451d25..d72119a4 100644
--- a/crates/app/src/auth/mod.rs
+++ b/crates/app/src/auth/mod.rs
@@ -6,3 +6,35 @@ mod tls;
pub use oidc::Claims as OidcClaims;
pub use tls::{Config as TlsConfig, TrustedCertificate};
+
+use super::{Repository, Store, User};
+
+use drawbridge_type::RepositoryContext;
+
+use axum::body::Body;
+use axum::extract::RequestParts;
+use axum::http::Request;
+use axum::response::IntoResponse;
+
+pub async fn assert_repository_read<'a>(
+ store: &'a Store,
+ cx: &'a RepositoryContext,
+ req: Request
,
+) -> Result<(Repository<'a>, Option>), impl IntoResponse> {
+ let repo = store.repository(cx);
+ if repo
+ .is_public()
+ .await
+ .map_err(IntoResponse::into_response)?
+ {
+ Ok((repo, None))
+ } else {
+ RequestParts::new(req)
+ .extract::()
+ .await?
+ .assert_user(store, &cx.owner)
+ .await
+ .map_err(IntoResponse::into_response)
+ .map(|user| (repo, Some(user)))
+ }
+}
diff --git a/crates/app/src/auth/oidc.rs b/crates/app/src/auth/oidc.rs
index fdedcea8..eacd2aa1 100644
--- a/crates/app/src/auth/oidc.rs
+++ b/crates/app/src/auth/oidc.rs
@@ -48,6 +48,26 @@ e.into_response()
}
})
}
+
+ /// Assert that the client is the user identified by `cx`.
+ pub async fn assert_user<'a>(
+ &self,
+ store: &'a Store,
+ cx: &UserContext,
+ ) -> Result, impl IntoResponse> {
+ let (ref oidc_cx, user) = self
+ .get_user(store)
+ .await
+ .map_err(IntoResponse::into_response)?;
+ if oidc_cx != cx {
+ return Err((
+ StatusCode::UNAUTHORIZED,
+ format!( "You are logged in as `{oidc_cx}`, please relogin as `{cx}` to access the resource"),
+ )
+ .into_response());
+ }
+ Ok(user)
+ }
}
#[async_trait]
diff --git a/crates/app/src/lib.rs b/crates/app/src/lib.rs
index 84570a28..6328d439 100644
--- a/crates/app/src/lib.rs
+++ b/crates/app/src/lib.rs
@@ -14,7 +14,7 @@ pub mod tags;
pub mod trees;
pub mod users;
-pub use auth::*;
+pub use auth::{OidcClaims, TlsConfig, TrustedCertificate};
pub use builder::*;
pub(crate) use handle::*;
pub(crate) use store::*;
diff --git a/crates/app/src/repos/get.rs b/crates/app/src/repos/get.rs
index 5a0772bf..7cec5618 100644
--- a/crates/app/src/repos/get.rs
+++ b/crates/app/src/repos/get.rs
@@ -6,30 +6,21 @@ use super::super::{OidcClaims, Store};
use drawbridge_type::RepositoryContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn get(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
cx: RepositoryContext,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
+ trace!(target: "app::trees::get", "called for `{cx}`");
+
+ let user = claims
+ .assert_user(store, &cx.owner)
.await
.map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.owner
- ),
- )
- .into_response());
- }
// TODO: Stream body
// https://github.com/profianinc/drawbridge/issues/56
diff --git a/crates/app/src/repos/head.rs b/crates/app/src/repos/head.rs
index e4f8cf1a..2d87c779 100644
--- a/crates/app/src/repos/head.rs
+++ b/crates/app/src/repos/head.rs
@@ -6,32 +6,22 @@ use super::super::{OidcClaims, Store};
use drawbridge_type::RepositoryContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn head(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
cx: RepositoryContext,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
- .await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.owner
- ),
- )
- .into_response());
- }
+ trace!(target: "app::trees::head", "called for `{cx}`");
- user.repository(&cx.name)
+ claims
+ .assert_user(store, &cx.owner)
+ .await
+ .map_err(IntoResponse::into_response)?
+ .repository(&cx.name)
.get_meta()
.await
.map_err(|e| {
diff --git a/crates/app/src/repos/put.rs b/crates/app/src/repos/put.rs
index 35a520d3..7817ff70 100644
--- a/crates/app/src/repos/put.rs
+++ b/crates/app/src/repos/put.rs
@@ -9,31 +9,22 @@ use async_std::sync::Arc;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{Extension, Json};
-use log::debug;
+use log::{debug, trace};
pub async fn put(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
cx: RepositoryContext,
meta: Meta,
Json(config): Json,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
- .await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.owner
- ),
- )
- .into_response());
- }
+ trace!(target: "app::trees::put", "called for `{cx}`");
- user.create_repository(&cx.name, meta, &config)
+ claims
+ .assert_user(store, &cx.owner)
+ .await
+ .map_err(IntoResponse::into_response)?
+ .create_repository(&cx.name, meta, &config)
.await
.map_err(|e| {
debug!(target: "app::repos::put", "failed for `{cx}`: {:?}", e);
diff --git a/crates/app/src/store/entity.rs b/crates/app/src/store/entity.rs
index 47e42c3a..14de7282 100644
--- a/crates/app/src/store/entity.rs
+++ b/crates/app/src/store/entity.rs
@@ -17,7 +17,7 @@ use futures::io::copy;
use futures::try_join;
use futures::{AsyncRead, AsyncWrite};
use log::{debug, trace};
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
const STORAGE_FAILURE_RESPONSE: (StatusCode, &str) =
(StatusCode::INTERNAL_SERVER_ERROR, "Storage backend failure");
@@ -301,7 +301,7 @@ impl<'a, P: AsRef> Entity<'a, P> {
})
}
- /// Returns metadata of an entity
+ /// Returns metadata of the entity.
pub async fn get_meta(&self) -> Result> {
let buf = self
.root
@@ -316,21 +316,49 @@ impl<'a, P: AsRef> Entity<'a, P> {
.map_err(GetError::Internal)
}
- /// Returns metadata of an entity and a reader of its contents.
+ /// Returns contents of the entity as [AsyncRead].
+ pub async fn get_content(&self) -> Result> {
+ self.root
+ .open(self.content_path())
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::NotFound => GetError::NotFound,
+ _ => {
+ GetError::Internal(anyhow::Error::new(e).context("failed to open content file"))
+ }
+ })
+ .await
+ }
+
+ /// Reads contents of the entity.
+ pub async fn read_content(&self) -> Result, GetError> {
+ self.root
+ .read(self.content_path())
+ .map_err(|e| match e.kind() {
+ io::ErrorKind::NotFound => GetError::NotFound,
+ _ => {
+ GetError::Internal(anyhow::Error::new(e).context("failed to read content file"))
+ }
+ })
+ .await
+ }
+
+ /// Returns the contents of the entity as JSON.
+ pub async fn get_content_json(&self) -> Result>
+ where
+ for<'de> T: Deserialize<'de>,
+ {
+ let buf = self.read_content().await?;
+ serde_json::from_slice(&buf)
+ .context("failed to decode content as JSON")
+ .map_err(GetError::Internal)
+ }
+
+ /// Returns metadata of the entity and a reader of its contents.
pub async fn get(&self) -> Result<(Meta, impl '_ + AsyncRead), GetError> {
- try_join!(
- self.get_meta(),
- self.root
- .open(self.content_path())
- .map_err(|e| match e.kind() {
- io::ErrorKind::NotFound => GetError::NotFound,
- _ => GetError::Internal(
- anyhow::Error::new(e).context("failed to open content file")
- ),
- })
- )
+ try_join!(self.get_meta(), self.get_content())
}
+ /// Returns metadata of the entity and writes its contents into `dst`.
pub async fn get_to_writer(
&self,
dst: &mut (impl Unpin + AsyncWrite),
diff --git a/crates/app/src/store/repo.rs b/crates/app/src/store/repo.rs
index a939931f..fe9175f8 100644
--- a/crates/app/src/store/repo.rs
+++ b/crates/app/src/store/repo.rs
@@ -6,7 +6,7 @@ use super::{CreateError, Entity, GetError, Tag};
use std::ops::Deref;
use drawbridge_type::digest::{Algorithms, ContentDigest};
-use drawbridge_type::{Meta, TagEntry, TagName};
+use drawbridge_type::{Meta, RepositoryConfig, TagEntry, TagName};
use anyhow::{anyhow, Context};
use camino::{Utf8Path, Utf8PathBuf};
@@ -30,6 +30,15 @@ impl<'a, P> From> for Repository<'a, P> {
}
impl<'a, P: AsRef> Repository<'a, P> {
+ pub async fn get_json(&self) -> Result> {
+ self.get_content_json().await
+ }
+
+ pub async fn is_public(&self) -> Result> {
+ let conf = self.get_json().await?;
+ Ok(conf.public)
+ }
+
pub async fn tags(&self) -> Result, GetError> {
self.read_dir("tags")
.await?
diff --git a/crates/app/src/tags/get.rs b/crates/app/src/tags/get.rs
index 53ad02b8..19298b51 100644
--- a/crates/app/src/tags/get.rs
+++ b/crates/app/src/tags/get.rs
@@ -1,41 +1,33 @@
// SPDX-FileCopyrightText: 2022 Profian Inc.
// SPDX-License-Identifier: AGPL-3.0-only
-use super::super::{OidcClaims, Store};
+use super::super::Store;
+use crate::auth::assert_repository_read;
use drawbridge_type::TagContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
+use axum::body::Body;
+use axum::http::Request;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn get(
- Extension(store): Extension>,
- claims: OidcClaims,
+ Extension(ref store): Extension>,
cx: TagContext,
+ req: Request,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
+ trace!(target: "app::tags::get", "called for `{cx}`");
+
+ let (repo, _) = assert_repository_read(store, &cx.repository, req)
.await
.map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.repository.owner
- ),
- )
- .into_response());
- }
// TODO: Stream body
// https://github.com/profianinc/drawbridge/issues/56
let mut body = vec![];
- user.repository(&cx.repository.name)
- .tag(&cx.name)
+ repo.tag(&cx.name)
.get_to_writer(&mut body)
.await
.map_err(|e| {
diff --git a/crates/app/src/tags/head.rs b/crates/app/src/tags/head.rs
index cb183b84..49b2748e 100644
--- a/crates/app/src/tags/head.rs
+++ b/crates/app/src/tags/head.rs
@@ -1,37 +1,29 @@
// SPDX-FileCopyrightText: 2022 Profian Inc.
// SPDX-License-Identifier: AGPL-3.0-only
-use super::super::{OidcClaims, Store};
+use super::super::Store;
+use crate::auth::assert_repository_read;
use drawbridge_type::TagContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
+use axum::body::Body;
+use axum::http::Request;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn head(
- Extension(store): Extension>,
- claims: OidcClaims,
+ Extension(ref store): Extension>,
cx: TagContext,
+ req: Request,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
- .await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.repository.owner
- ),
- )
- .into_response());
- }
+ trace!(target: "app::tags::head", "called for `{cx}`");
- user.repository(&cx.repository.name)
+ assert_repository_read(store, &cx.repository, req)
+ .await
+ .map_err(IntoResponse::into_response)
+ .map(|(repo, _)| repo)?
.tag(&cx.name)
.get_meta()
.await
diff --git a/crates/app/src/tags/put.rs b/crates/app/src/tags/put.rs
index 1f54e8a6..c74501d4 100644
--- a/crates/app/src/tags/put.rs
+++ b/crates/app/src/tags/put.rs
@@ -13,7 +13,7 @@ use axum::extract::RequestParts;
use axum::http::{Request, StatusCode};
use axum::response::IntoResponse;
use axum::{Extension, Json};
-use log::debug;
+use log::{debug, trace};
pub async fn put(
Extension(store): Extension>,
@@ -22,6 +22,8 @@ pub async fn put(
meta: Meta,
req: Request,
) -> impl IntoResponse {
+ trace!(target: "app::tags::put", "called for `{cx}`");
+
if meta.hash.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
@@ -30,20 +32,10 @@ pub async fn put(
.into_response());
}
- let (oidc_cx, user) = claims
- .get_user(&store)
+ let user = claims
+ .assert_user(&store, &cx.repository.owner)
.await
.map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.repository.owner
- ),
- )
- .into_response());
- }
let mut req = RequestParts::new(req);
let entry = match meta.mime.to_string().as_str() {
diff --git a/crates/app/src/tags/query.rs b/crates/app/src/tags/query.rs
index da8ec4e8..56c3499d 100644
--- a/crates/app/src/tags/query.rs
+++ b/crates/app/src/tags/query.rs
@@ -1,38 +1,30 @@
// SPDX-FileCopyrightText: 2022 Profian Inc.
// SPDX-License-Identifier: AGPL-3.0-only
-use super::super::{OidcClaims, Store};
+use super::super::Store;
+use crate::auth::assert_repository_read;
use drawbridge_type::{Meta, RepositoryContext};
use async_std::sync::Arc;
-use axum::http::StatusCode;
+use axum::body::Body;
+use axum::http::Request;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
use mime::APPLICATION_JSON;
pub async fn query(
- Extension(store): Extension>,
- claims: OidcClaims,
- cx: RepositoryContext,
+ Extension(ref store): Extension>,
+ ref cx: RepositoryContext,
+ req: Request,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
- .await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.owner
- ),
- )
- .into_response());
- }
+ trace!(target: "app::tags::query", "called for `{cx}`");
- user.repository(&cx.name)
+ assert_repository_read(store, cx, req)
+ .await
+ .map_err(IntoResponse::into_response)
+ .map(|(repo, _)| repo)?
.tags_json()
.await
.map(|(hash, buf)| {
diff --git a/crates/app/src/trees/get.rs b/crates/app/src/trees/get.rs
index 5827fc7f..c8a37b47 100644
--- a/crates/app/src/trees/get.rs
+++ b/crates/app/src/trees/get.rs
@@ -1,52 +1,39 @@
// SPDX-FileCopyrightText: 2022 Profian Inc.
// SPDX-License-Identifier: AGPL-3.0-only
-use super::super::{OidcClaims, Store, TrustedCertificate};
+use super::super::{Store, TrustedCertificate};
+use crate::auth::assert_repository_read;
use drawbridge_type::TreeContext;
use async_std::sync::Arc;
use axum::body::Body;
-use axum::extract::RequestParts;
-use axum::http::{Request, StatusCode};
+use axum::http::Request;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn get(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
cert: Option>,
cx: TreeContext,
req: Request,
) -> impl IntoResponse {
- // TODO: Check if repo is public
- let user = if cert.is_none() {
- let (oidc_cx, user) = RequestParts::new(req)
- .extract::()
- .await?
- .get_user(&store)
+ trace!(target: "app::trees::get", "called for `{cx}`");
+
+ let repo = if cert.is_none() {
+ assert_repository_read(store, &cx.tag.repository, req)
.await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.tag.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.tag.repository.owner
- ),
- )
- .into_response());
- }
- user
+ .map_err(IntoResponse::into_response)
+ .map(|(repo, _)| repo)?
} else {
- store.user(&cx.tag.repository.owner)
+ store.repository(&cx.tag.repository)
};
// TODO: Stream body
// https://github.com/profianinc/drawbridge/issues/56
let mut body = vec![];
- user.repository(&cx.tag.repository.name)
- .tag(&cx.tag.name)
+ repo.tag(&cx.tag.name)
.node(&cx.path)
.get_to_writer(&mut body)
.await
diff --git a/crates/app/src/trees/head.rs b/crates/app/src/trees/head.rs
index 8568516f..6c06cf7f 100644
--- a/crates/app/src/trees/head.rs
+++ b/crates/app/src/trees/head.rs
@@ -1,55 +1,41 @@
// SPDX-FileCopyrightText: 2022 Profian Inc.
// SPDX-License-Identifier: AGPL-3.0-only
-use super::super::{OidcClaims, Store, TrustedCertificate};
+use super::super::{Store, TrustedCertificate};
+use crate::auth::assert_repository_read;
use drawbridge_type::TreeContext;
use async_std::sync::Arc;
use axum::body::Body;
-use axum::extract::RequestParts;
-use axum::http::{Request, StatusCode};
+use axum::http::Request;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn head(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
cert: Option>,
cx: TreeContext,
req: Request,
) -> impl IntoResponse {
- // TODO: Check if repo is public
- let user = if cert.is_none() {
- let (oidc_cx, user) = RequestParts::new(req)
- .extract::()
- .await?
- .get_user(&store)
+ trace!(target: "app::trees::head", "called for `{cx}`");
+
+ if cert.is_none() {
+ assert_repository_read(store, &cx.tag.repository, req)
.await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.tag.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.tag.repository.owner
- ),
- )
- .into_response());
- }
- user
+ .map_err(IntoResponse::into_response)
+ .map(|(repo, _)| repo)?
} else {
- store.user(&cx.tag.repository.owner)
- };
-
- user.repository(&cx.tag.repository.name)
- .tag(&cx.tag.name)
- .node(&cx.path)
- .get_meta()
- .await
- .map_err(|e| {
- debug!(target: "app::trees::head", "failed for `{cx}`: {:?}", e);
- e.into_response()
- })
- .map(|meta| (meta, ()))
+ store.repository(&cx.tag.repository)
+ }
+ .tag(&cx.tag.name)
+ .node(&cx.path)
+ .get_meta()
+ .await
+ .map_err(|e| {
+ debug!(target: "app::trees::head", "failed for `{cx}`: {:?}", e);
+ e.into_response()
+ })
+ .map(|meta| (meta, ()))
}
diff --git a/crates/app/src/trees/put.rs b/crates/app/src/trees/put.rs
index f67a6d85..dd3e3d81 100644
--- a/crates/app/src/trees/put.rs
+++ b/crates/app/src/trees/put.rs
@@ -12,15 +12,17 @@ use axum::http::{Request, StatusCode};
use axum::response::IntoResponse;
use axum::{Extension, Json};
use futures::{io, TryStreamExt};
-use log::debug;
+use log::{debug, trace};
pub async fn put(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
cx: TreeContext,
meta: Meta,
req: Request,
) -> impl IntoResponse {
+ trace!(target: "app::trees::put", "called for `{cx}`");
+
if meta.hash.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
@@ -29,20 +31,10 @@ pub async fn put(
.into_response());
}
- let (oidc_cx, user) = claims
- .get_user(&store)
+ let user = claims
+ .assert_user(store, &cx.tag.repository.owner)
.await
.map_err(IntoResponse::into_response)?;
- if oidc_cx != cx.tag.repository.owner {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx.tag.repository.owner
- ),
- )
- .into_response());
- }
let mut req = RequestParts::new(req);
let tag = user.repository(&cx.tag.repository.name).tag(&cx.tag.name);
diff --git a/crates/app/src/users/get.rs b/crates/app/src/users/get.rs
index b1d1e2cf..b36b25b8 100644
--- a/crates/app/src/users/get.rs
+++ b/crates/app/src/users/get.rs
@@ -6,30 +6,21 @@ use super::super::{OidcClaims, Store};
use drawbridge_type::UserContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn get(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
- cx: UserContext,
+ ref cx: UserContext,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
+ trace!(target: "app::users::get", "called for `{cx}`");
+
+ let user = claims
+ .assert_user(store, cx)
.await
.map_err(IntoResponse::into_response)?;
- if oidc_cx != cx {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx
- ),
- )
- .into_response());
- }
// TODO: Stream body
// https://github.com/profianinc/drawbridge/issues/56
diff --git a/crates/app/src/users/head.rs b/crates/app/src/users/head.rs
index db89cf00..bc939890 100644
--- a/crates/app/src/users/head.rs
+++ b/crates/app/src/users/head.rs
@@ -6,32 +6,22 @@ use super::super::{OidcClaims, Store};
use drawbridge_type::UserContext;
use async_std::sync::Arc;
-use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
-use log::debug;
+use log::{debug, trace};
pub async fn head(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
- cx: UserContext,
+ ref cx: UserContext,
) -> impl IntoResponse {
- let (oidc_cx, user) = claims
- .get_user(&store)
- .await
- .map_err(IntoResponse::into_response)?;
- if oidc_cx != cx {
- return Err((
- StatusCode::UNAUTHORIZED,
- format!(
- "You are logged in as `{oidc_cx}`, please relogin as `{}` to access `{cx}`",
- cx
- ),
- )
- .into_response());
- }
+ trace!(target: "app::users::head", "called for `{cx}`");
- user.get_meta()
+ claims
+ .assert_user(store, cx)
+ .await
+ .map_err(IntoResponse::into_response)?
+ .get_meta()
.await
.map_err(|e| {
debug!(target: "app::users::head", "failed for `{cx}`: {:?}", e);
diff --git a/crates/app/src/users/put.rs b/crates/app/src/users/put.rs
index bb06a320..1f71ef6d 100644
--- a/crates/app/src/users/put.rs
+++ b/crates/app/src/users/put.rs
@@ -9,15 +9,17 @@ use async_std::sync::Arc;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{Extension, Json};
-use log::{debug, warn};
+use log::{debug, trace, warn};
pub async fn put(
- Extension(store): Extension>,
+ Extension(ref store): Extension>,
claims: OidcClaims,
- cx: UserContext,
+ ref cx: UserContext,
meta: Meta,
- Json(record): Json,
+ Json(ref record): Json,
) -> impl IntoResponse {
+ trace!(target: "app::users::put", "called for `{cx}`");
+
if record.subject != claims.subject().as_str() {
return Err((StatusCode::UNAUTHORIZED, "OpenID Connect subject mismatch").into_response());
}
@@ -44,7 +46,7 @@ pub async fn put(
}
store
- .create_user(&cx, meta, &record)
+ .create_user(cx, meta, record)
.await
.map_err(|e| {
debug!(target: "app::users::put", "failed for `{cx}`: {:?}", e);
diff --git a/crates/type/src/repository/config.rs b/crates/type/src/repository/config.rs
index d0a943dc..114a51b9 100644
--- a/crates/type/src/repository/config.rs
+++ b/crates/type/src/repository/config.rs
@@ -4,6 +4,8 @@
use serde::{Deserialize, Serialize};
/// A repository config
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
-pub struct Config {}
+pub struct Config {
+ pub public: bool,
+}
diff --git a/tests/mod.rs b/tests/mod.rs
index 55f7dd1a..57f8c12d 100644
--- a/tests/mod.rs
+++ b/tests/mod.rs
@@ -202,45 +202,84 @@ async fn app() {
.expect("failed to create user"),
true
);
+ assert!(oidc_cl
+ .user(&format!("{user_name}other").parse().unwrap())
+ .create(&user_record)
+ .is_err());
assert!(anon_user.get().is_err());
assert!(cert_user.get().is_err());
assert_eq!(oidc_user.get().expect("failed to get user"), user_record);
- let repo_name = "test-repo-private".parse().unwrap();
- let repo_conf = RepositoryConfig {};
+ let prv_repo_name = "test-repo-private".parse().unwrap();
+ let prv_repo_conf = RepositoryConfig { public: false };
+
+ let pub_repo_name = "test-repo-public".parse().unwrap();
+ let pub_repo_conf = RepositoryConfig { public: true };
+
+ let anon_prv_repo = anon_user.repository(&prv_repo_name);
+ let cert_prv_repo = cert_user.repository(&prv_repo_name);
+ let oidc_prv_repo = oidc_user.repository(&prv_repo_name);
+
+ let anon_pub_repo = anon_user.repository(&pub_repo_name);
+ let cert_pub_repo = cert_user.repository(&pub_repo_name);
+ let oidc_pub_repo = oidc_user.repository(&pub_repo_name);
+
+ assert!(anon_prv_repo.get().is_err());
+ assert!(cert_prv_repo.get().is_err());
+ assert!(oidc_prv_repo.get().is_err());
- let anon_repo = anon_user.repository(&repo_name);
- let cert_repo = cert_user.repository(&repo_name);
- let oidc_repo = oidc_user.repository(&repo_name);
+ assert!(anon_pub_repo.get().is_err());
+ assert!(cert_pub_repo.get().is_err());
+ assert!(oidc_pub_repo.get().is_err());
- assert!(anon_repo.get().is_err());
- assert!(cert_repo.get().is_err());
- assert!(oidc_repo.get().is_err());
+ assert!(anon_prv_repo.tags().is_err());
+ assert!(cert_prv_repo.tags().is_err());
+ assert!(oidc_prv_repo.tags().is_err());
- assert!(anon_repo.tags().is_err());
- assert!(cert_repo.tags().is_err());
- assert!(oidc_repo.tags().is_err());
+ assert!(anon_pub_repo.tags().is_err());
+ assert!(cert_pub_repo.tags().is_err());
+ assert!(oidc_pub_repo.tags().is_err());
+
+ assert!(anon_prv_repo.create(&prv_repo_conf).is_err());
+ assert!(cert_prv_repo.create(&prv_repo_conf).is_err());
+ assert_eq!(
+ oidc_prv_repo
+ .create(&prv_repo_conf)
+ .expect("failed to create repository"),
+ true
+ );
- assert!(anon_repo.create(&repo_conf).is_err());
- assert!(cert_repo.create(&repo_conf).is_err());
+ assert!(anon_pub_repo.create(&pub_repo_conf).is_err());
+ assert!(cert_pub_repo.create(&pub_repo_conf).is_err());
assert_eq!(
- oidc_repo
- .create(&repo_conf)
+ oidc_pub_repo
+ .create(&pub_repo_conf)
.expect("failed to create repository"),
true
);
- assert!(anon_repo.get().is_err());
- assert!(cert_repo.get().is_err());
+ assert!(anon_prv_repo.get().is_err());
+ assert!(cert_prv_repo.get().is_err());
assert_eq!(
- oidc_repo.get().expect("failed to get repository"),
- repo_conf
+ oidc_prv_repo.get().expect("failed to get repository"),
+ prv_repo_conf
);
- assert!(anon_repo.tags().is_err());
- assert!(cert_repo.tags().is_err());
- assert_eq!(oidc_repo.tags().expect("failed to get tags"), vec![]);
+ assert!(anon_pub_repo.get().is_err());
+ assert!(cert_pub_repo.get().is_err());
+ assert_eq!(
+ oidc_pub_repo.get().expect("failed to get repository"),
+ pub_repo_conf
+ );
+
+ assert!(anon_prv_repo.tags().is_err());
+ assert!(cert_prv_repo.tags().is_err());
+ assert_eq!(oidc_prv_repo.tags().expect("failed to get tags"), vec![]);
+
+ assert_eq!(anon_pub_repo.tags().expect("failed to get tags"), vec![]);
+ assert_eq!(cert_pub_repo.tags().expect("failed to get tags"), vec![]);
+ assert_eq!(oidc_pub_repo.tags().expect("failed to get tags"), vec![]);
let pkg = tempdir().expect("failed to create temporary package directory");
@@ -273,22 +312,30 @@ async fn app() {
let tag_name = "0.1.0".parse().unwrap();
- let anon_tag = anon_repo.tag(&tag_name);
- let cert_tag = cert_repo.tag(&tag_name);
- let oidc_tag = oidc_repo.tag(&tag_name);
+ let anon_prv_tag = anon_prv_repo.tag(&tag_name);
+ let cert_prv_tag = cert_prv_repo.tag(&tag_name);
+ let oidc_prv_tag = oidc_prv_repo.tag(&tag_name);
+
+ let anon_pub_tag = anon_pub_repo.tag(&tag_name);
+ let cert_pub_tag = cert_pub_repo.tag(&tag_name);
+ let oidc_pub_tag = oidc_pub_repo.tag(&tag_name);
- assert!(anon_tag.get().is_err());
- assert!(cert_tag.get().is_err());
- assert!(oidc_tag.get().is_err());
+ assert!(anon_prv_tag.get().is_err());
+ assert!(cert_prv_tag.get().is_err());
+ assert!(oidc_prv_tag.get().is_err());
- assert!(anon_tag.create_from_path_unsigned(pkg.path()).is_err());
- assert!(cert_tag.create_from_path_unsigned(pkg.path()).is_err());
- let (tag_created, tree_created) = oidc_tag
+ assert!(anon_pub_tag.get().is_err());
+ assert!(cert_pub_tag.get().is_err());
+ assert!(oidc_pub_tag.get().is_err());
+
+ assert!(anon_prv_tag.create_from_path_unsigned(pkg.path()).is_err());
+ assert!(cert_prv_tag.create_from_path_unsigned(pkg.path()).is_err());
+ let (prv_tag_created, prv_tree_created) = oidc_prv_tag
.create_from_path_unsigned(pkg.path())
.expect("failed to create a tag and upload the tree");
- assert!(tag_created);
+ assert!(prv_tag_created);
assert_eq!(
- tree_created.clone().into_iter().collect::>(),
+ prv_tree_created.clone().into_iter().collect::>(),
vec![
(TreePath::ROOT, true),
("tEst-file..__.foo.42.".parse().unwrap(), true),
@@ -304,26 +351,65 @@ async fn app() {
]
);
- assert!(anon_repo.tags().is_err());
- assert!(cert_repo.tags().is_err());
+ assert!(anon_pub_tag.create_from_path_unsigned(pkg.path()).is_err());
+ assert!(cert_pub_tag.create_from_path_unsigned(pkg.path()).is_err());
+ assert_eq!(
+ oidc_pub_tag
+ .create_from_path_unsigned(pkg.path())
+ .expect("failed to create a tag and upload the tree"),
+ (prv_tag_created, prv_tree_created)
+ );
+
+ assert!(anon_prv_repo.tags().is_err());
+ assert!(cert_prv_repo.tags().is_err());
+ assert_eq!(
+ oidc_prv_repo.tags().expect("failed to get tags"),
+ vec![tag_name.clone()]
+ );
+
+ assert_eq!(
+ anon_pub_repo.tags().expect("failed to get tags"),
+ vec![tag_name.clone()]
+ );
assert_eq!(
- oidc_repo.tags().expect("failed to get tags"),
+ cert_pub_repo.tags().expect("failed to get tags"),
+ vec![tag_name.clone()]
+ );
+ assert_eq!(
+ oidc_pub_repo.tags().expect("failed to get tags"),
vec![tag_name.clone()]
);
let file_name = "test-file.txt".parse().unwrap();
- let anon_file = anon_tag.path(&file_name);
- let cert_file = cert_tag.path(&file_name);
- let oidc_file = oidc_tag.path(&file_name);
+ let anon_prv_file = anon_prv_tag.path(&file_name);
+ let cert_prv_file = cert_prv_tag.path(&file_name);
+ let oidc_prv_file = oidc_prv_tag.path(&file_name);
+
+ let anon_pub_file = anon_pub_tag.path(&file_name);
+ let cert_pub_file = cert_pub_tag.path(&file_name);
+ let oidc_pub_file = oidc_pub_tag.path(&file_name);
- assert!(anon_file.get_string().is_err());
+ assert!(anon_prv_file.get_string().is_err());
+ assert_eq!(
+ cert_prv_file.get_string().expect("failed to get file"),
+ (APPLICATION_OCTET_STREAM, "text".into())
+ );
+ assert_eq!(
+ oidc_prv_file.get_string().expect("failed to get file"),
+ (APPLICATION_OCTET_STREAM, "text".into())
+ );
+
+ assert_eq!(
+ anon_pub_file.get_string().expect("failed to get file"),
+ (APPLICATION_OCTET_STREAM, "text".into())
+ );
assert_eq!(
- cert_file.get_string().expect("failed to get file"),
+ cert_pub_file.get_string().expect("failed to get file"),
(APPLICATION_OCTET_STREAM, "text".into())
);
assert_eq!(
- oidc_file.get_string().expect("failed to get file"),
+ oidc_pub_file.get_string().expect("failed to get file"),
(APPLICATION_OCTET_STREAM, "text".into())
);
});