Skip to content

Commit

Permalink
feat(api): add basic authentication checks
Browse files Browse the repository at this point in the history
  • Loading branch information
lennoxlotl committed Oct 21, 2024
1 parent e76e78b commit 1560e29
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 1 deletion.
3 changes: 3 additions & 0 deletions api/src/endpoint/v1/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub struct ErrorAttributes {

#[derive(Debug, Clone, thiserror::Error, UploaderError)]
pub enum Error {
#[error("Invalid auth key")]
#[uploader(status_code = 403)]
Unauthorized,
#[error("The uploaded image is too large")]
#[uploader(status_code = 403)]
ImageTooLargeError,
Expand Down
28 changes: 27 additions & 1 deletion api/src/endpoint/v1/image/upload.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
use std::convert::Infallible;

use rand::distributions::Alphanumeric;
use rand::Rng;
use rocket::form::{Form, FromForm};
use rocket::fs::TempFile;
use rocket::http::ContentType;
use rocket::outcome::Outcome;
use rocket::request::{self, FromRequest};
use rocket::serde::json::Json;
use rocket::{post, Responder, State};
use rocket::{post, Request, Responder, State};
use serde::Serialize;
use uuid::Uuid;

Expand All @@ -21,6 +25,8 @@ pub struct ImageData<'r> {
image: TempFile<'r>,
}

pub struct AuthToken(String);

#[derive(Responder)]
#[response(status = 200, content_type = "json")]
pub struct UploadResponse {
Expand All @@ -41,18 +47,38 @@ impl UploadResponse {
}
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthToken {
type Error = Infallible;

async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let token = request.headers().get_one("Authorization");
match token {
Some(token) => Outcome::Success(AuthToken(token.to_string())),
None => Outcome::Success(AuthToken("".into())),
}
}
}

#[post("/image/upload", data = "<image_data>")]
pub async fn upload(
image_data: Form<ImageData<'_>>,
bucket: BucketGuard,
database: PostgresDb,
config: &State<GlobalConfig>,
token: AuthToken,
) -> UploaderResult<UploadResponse> {
let mut transaction = database.begin().await.map_err(|_| Error::DatabaseError)?;
let bucket_id = Uuid::new_v4().to_string().replace("-", "");
let secret = Uuid::new_v4().to_string().replace("-", "");
let id = generate_image_id(config.image_id_length);

if let Some(auth_key) = &config.auth_key {
if auth_key != &token.0 {
return Err(Error::Unauthorized);
}
}

// As we use transactions, if the image upload fails the image will be dropped
save_image(
&mut transaction,
Expand Down
2 changes: 2 additions & 0 deletions api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub struct GlobalConfig {
public_url: String,
// Length of the image id used for "shwoing" the image
image_id_length: usize,
// If not empty requires an authentication header containing this key for uploads
auth_key: Option<String>,
}

#[rocket::main]
Expand Down

0 comments on commit 1560e29

Please sign in to comment.