aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCara Salter <cara@devcara.com>2022-06-28 07:07:34 -0400
committerCara Salter <cara@devcara.com>2022-06-28 07:07:34 -0400
commit8de1eae2b49d763dcac55b8a2a84673475a35e63 (patch)
tree3b2d385b590c4211cf311042122563b89dcc6e51 /src
parentf516a9616b7160be149ef4ba8726557e019bf621 (diff)
downloadsolard-8de1eae2b49d763dcac55b8a2a84673475a35e63.tar.gz
solard-8de1eae2b49d763dcac55b8a2a84673475a35e63.zip
auth: Scaffold auth/begin [WIP]
Diffstat (limited to 'src')
-rw-r--r--src/errors.rs12
-rw-r--r--src/handlers/auth.rs34
-rw-r--r--src/handlers/mod.rs1
-rw-r--r--src/main.rs21
4 files changed, 67 insertions, 1 deletions
diff --git a/src/errors.rs b/src/errors.rs
index e0a6df9..cbec046 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -20,7 +20,16 @@ pub enum ServiceError {
Reqwest(#[from] reqwest::Error),
#[error("Command error: {0}")]
- Command(#[from] std::io::Error)
+ Command(#[from] std::io::Error),
+
+ #[error("Not authorized")]
+ NotAuthorized,
+
+ #[error("Generic: {0}")]
+ Generic(String),
+
+ #[error("Paseto: {0}")]
+ Paseto(#[from] pasetors::errors::Error),
}
pub type StringResult<T = &'static str> = std::result::Result<T, ServiceError>;
@@ -35,6 +44,7 @@ impl IntoResponse for ServiceError {
let status = match self {
ServiceError::NotFound => StatusCode::NOT_FOUND,
+ ServiceError::NotAuthorized => StatusCode::UNAUTHORIZED,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
Response::builder()
diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs
new file mode 100644
index 0000000..a9ac394
--- /dev/null
+++ b/src/handlers/auth.rs
@@ -0,0 +1,34 @@
+use std::collections::HashMap;
+
+use axum::{extract::Query, Extension};
+use axum_macros::debug_handler;
+use pasetors::{claims::Claims, keys::{AsymmetricKeyPair, Generate}, version4::V4};
+use uuid::Uuid;
+
+use crate::{errors::{NoneResult, ServiceError}, State};
+
+
+/**
+ * Takes in a request to create a new token with a secret key that gets printed
+ * to stdout and, if it matches, returns a valid PASETO token that can be used
+ * for future authentication
+ */
+#[debug_handler]
+pub async fn begin(Query(params): Query<HashMap<String, String>>, Extension(state): Extension<State>) -> NoneResult {
+ if let Some(k) = params.get("key") {
+ if k == &state.gen_key {
+ let mut claims = Claims::new()?;
+ claims.non_expiring();
+ claims.audience("solard")?;
+ claims.add_additional("uuid", Uuid::new_v4().to_string())?;
+
+ let kp = AsymmetricKeyPair::<V4>::generate()?;
+ } else {
+ return Err(ServiceError::NotAuthorized);
+ }
+ } else {
+ return Err(ServiceError::Generic("No key supplied".to_string()));
+ }
+
+ Ok(())
+}
diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs
index 591f76e..8f8224e 100644
--- a/src/handlers/mod.rs
+++ b/src/handlers/mod.rs
@@ -1 +1,2 @@
pub mod planets;
+pub mod auth;
diff --git a/src/main.rs b/src/main.rs
index 4dc78a6..3f889cd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7,6 +7,7 @@ use axum::{
Json, Router, Extension
};
+use rand::{thread_rng, Rng, distributions::Alphanumeric};
use serde::{Deserialize, Serialize};
use solarlib::star::Star;
use std::{net::SocketAddr, time::Duration, str::FromStr, sync::Arc};
@@ -20,8 +21,11 @@ mod errors;
mod handlers;
+#[derive(Clone)]
pub struct State {
pub hw_url: String,
+ pub secret_key: String,
+ pub gen_key: String,
}
#[tokio::main]
@@ -36,10 +40,25 @@ async fn main() {
.with(tracing_subscriber::fmt::layer())
.init();
+ let rand_key: String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .take(30)
+ .map(char::from)
+ .collect();
+
let shared_state = Arc::new(State {
hw_url: std::env::var("HOMEWORLD_URL").expect("No Homeworld URL set"),
+ secret_key: std::env::var("SECRET_KEY").unwrap_or("bad-key".to_string()),
+ gen_key: rand_key,
});
+ if shared_state.secret_key == "bad-key" {
+ tracing::warn!("No secret key set! This is a bad idea.");
+ tracing::warn!("Using default of `bad-key`");
+ }
+
+ tracing::info!("Random Key: {}", shared_state.gen_key);
+
let app = Router::new()
.route("/health", get(health_check))
.route("/planets/list", get(handlers::planets::list))
@@ -52,6 +71,8 @@ async fn main() {
.route("/planets/:uuid/reboot", post(handlers::planets::reboot))
.route("/planets/:uuid/reboot/hard", post(handlers::planets::force_reboot))
.route("/planets/:uuid/destroy", post(handlers::planets::no_planet))
+ // Authentication
+ .route("/auth/begin", post(handlers::auth::begin))
.layer( ServiceBuilder::new()
.layer(HandleErrorLayer::new(|error: BoxError| async move {
if error.is::<tower::timeout::error::Elapsed>() {