From f5f407b053df0f6140f110e2bd7bbf1856bff160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oddbj=C3=B8rn=20Gr=C3=B8dem?= <29732646+oddgrd@users.noreply.github.com> Date: Mon, 8 May 2023 19:05:45 +0200 Subject: [PATCH] fix: revert addition of apikey to auth (#886) * fix: revert addition of apikey to auth * fix: display impl is needed for key.to_string() --- auth/Cargo.toml | 2 +- auth/src/api/handlers.rs | 2 +- auth/src/lib.rs | 17 +++-- auth/src/user.rs | 62 +++++++++++-------- cargo-shuttle/src/provisioner_server.rs | 17 +++++ common/src/database.rs | 2 + deployer/src/persistence/resource/database.rs | 4 ++ provisioner/src/lib.rs | 3 + resources/sqlite/Cargo.toml | 12 ++++ resources/sqlite/src/lib.rs | 49 +++++++++++++++ 10 files changed, 133 insertions(+), 37 deletions(-) create mode 100644 resources/sqlite/Cargo.toml create mode 100644 resources/sqlite/src/lib.rs diff --git a/auth/Cargo.toml b/auth/Cargo.toml index 8b3d7171ad..9289f479e8 100644 --- a/auth/Cargo.toml +++ b/auth/Cargo.toml @@ -32,7 +32,7 @@ tracing-subscriber = { workspace = true } [dependencies.shuttle-common] workspace = true -features = ["backend", "models", "persist"] +features = ["backend", "models"] [dev-dependencies] axum-extra = { version = "0.7.1", features = ["cookie"] } diff --git a/auth/src/api/handlers.rs b/auth/src/api/handlers.rs index e96325ed5c..c441015c04 100644 --- a/auth/src/api/handlers.rs +++ b/auth/src/api/handlers.rs @@ -92,7 +92,7 @@ pub(crate) async fn convert_key( let User { name, account_tier, .. } = user_manager - .get_user_by_key(key.as_ref().clone()) + .get_user_by_key(key) .await .map_err(|_| StatusCode::UNAUTHORIZED)?; diff --git a/auth/src/lib.rs b/auth/src/lib.rs index 2e3aa333f1..3e73a24711 100644 --- a/auth/src/lib.rs +++ b/auth/src/lib.rs @@ -7,7 +7,6 @@ mod user; use std::{io, str::FromStr, time::Duration}; use args::StartArgs; -use shuttle_common::ApiKey; use sqlx::{ migrate::Migrator, query, @@ -16,7 +15,10 @@ use sqlx::{ }; use tracing::info; -use crate::{api::serve, user::AccountTier}; +use crate::{ + api::serve, + user::{AccountTier, Key}, +}; pub use api::ApiBuilder; pub use args::{Args, Commands, InitArgs}; @@ -39,8 +41,8 @@ pub async fn start(pool: SqlitePool, args: StartArgs) -> io::Result<()> { pub async fn init(pool: SqlitePool, args: InitArgs) -> io::Result<()> { let key = match args.key { - Some(ref key) => ApiKey::parse(key).unwrap(), - None => ApiKey::generate(), + Some(ref key) => Key::from_str(key).unwrap(), + None => Key::new_random(), }; query("INSERT INTO users (account_name, key, account_tier) VALUES (?1, ?2, ?3)") @@ -51,11 +53,8 @@ pub async fn init(pool: SqlitePool, args: InitArgs) -> io::Result<()> { .await .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; - println!( - "`{}` created as super user with key: {}", - args.name, - key.as_ref() - ); + println!("`{}` created as super user with key: {key}", args.name,); + Ok(()) } diff --git a/auth/src/user.rs b/auth/src/user.rs index fba965bbb5..5ba3ec1b6a 100644 --- a/auth/src/user.rs +++ b/auth/src/user.rs @@ -7,11 +7,9 @@ use axum::{ http::request::Parts, TypedHeader, }; +use rand::distributions::{Alphanumeric, DistString}; use serde::{Deserialize, Deserializer, Serialize}; -use shuttle_common::{ - claims::{Scope, ScopeBuilder}, - ApiKey, -}; +use shuttle_common::claims::{Scope, ScopeBuilder}; use sqlx::{query, Row, SqlitePool}; use tracing::{trace, Span}; @@ -21,7 +19,7 @@ use crate::{api::UserManagerState, error::Error}; pub trait UserManagement: Send + Sync { async fn create_user(&self, name: AccountName, tier: AccountTier) -> Result; async fn get_user(&self, name: AccountName) -> Result; - async fn get_user_by_key(&self, key: ApiKey) -> Result; + async fn get_user_by_key(&self, key: Key) -> Result; } #[derive(Clone)] @@ -32,7 +30,7 @@ pub struct UserManager { #[async_trait] impl UserManagement for UserManager { async fn create_user(&self, name: AccountName, tier: AccountTier) -> Result { - let key = ApiKey::generate(); + let key = Key::new_random(); query("INSERT INTO users (account_name, key, account_tier) VALUES (?1, ?2, ?3)") .bind(&name) @@ -57,7 +55,7 @@ impl UserManagement for UserManager { .ok_or(Error::UserNotFound) } - async fn get_user_by_key(&self, key: ApiKey) -> Result { + async fn get_user_by_key(&self, key: Key) -> Result { query("SELECT account_name, key, account_tier FROM users WHERE key = ?1") .bind(&key) .fetch_optional(&self.pool) @@ -71,10 +69,10 @@ impl UserManagement for UserManager { } } -#[derive(Clone, Deserialize, PartialEq, Eq, Serialize, Debug)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct User { pub name: AccountName, - pub key: ApiKey, + pub key: Key, pub account_tier: AccountTier, } @@ -83,7 +81,7 @@ impl User { self.account_tier == AccountTier::Admin } - pub fn new(name: AccountName, key: ApiKey, account_tier: AccountTier) -> Self { + pub fn new(name: AccountName, key: Key, account_tier: AccountTier) -> Self { Self { name, key, @@ -106,7 +104,7 @@ where let user_manager: UserManagerState = UserManagerState::from_ref(state); let user = user_manager - .get_user_by_key(key.as_ref().clone()) + .get_user_by_key(key) .await // Absorb any error into `Unauthorized` .map_err(|_| Error::Unauthorized)?; @@ -122,21 +120,16 @@ impl From for shuttle_common::models::user::Response { fn from(user: User) -> Self { Self { name: user.name.to_string(), - key: user.key.as_ref().to_string(), + key: user.key.to_string(), account_tier: user.account_tier.to_string(), } } } -/// A wrapper around [ApiKey] so we can implement [FromRequestParts] -/// for it. -pub struct Key(ApiKey); - -impl AsRef for Key { - fn as_ref(&self) -> &ApiKey { - &self.0 - } -} +#[derive(Clone, sqlx::Type, PartialEq, Hash, Eq, Serialize, Deserialize, Debug)] +#[serde(transparent)] +#[sqlx(transparent)] +pub struct Key(String); #[async_trait] impl FromRequestParts for Key @@ -149,14 +142,31 @@ where let key = TypedHeader::>::from_request_parts(parts, state) .await .map_err(|_| Error::KeyMissing) - .and_then(|TypedHeader(Authorization(bearer))| { - let bearer = bearer.token().trim(); - ApiKey::parse(bearer).map_err(|_| Self::Rejection::Unauthorized) - })?; + .and_then(|TypedHeader(Authorization(bearer))| bearer.token().trim().parse())?; trace!("got bearer key"); - Ok(Key(key)) + Ok(key) + } +} + +impl std::fmt::Display for Key { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl FromStr for Key { + type Err = Error; + + fn from_str(s: &str) -> Result { + Ok(Self(s.to_string())) + } +} + +impl Key { + pub fn new_random() -> Self { + Self(Alphanumeric.sample_string(&mut rand::thread_rng(), 16)) } } diff --git a/cargo-shuttle/src/provisioner_server.rs b/cargo-shuttle/src/provisioner_server.rs index 09308ff438..c6b7811396 100644 --- a/cargo-shuttle/src/provisioner_server.rs +++ b/cargo-shuttle/src/provisioner_server.rs @@ -410,5 +410,22 @@ fn db_type_to_config(db_type: Type) -> EngineConfig { "show databases;".to_string(), ], }, + Type::Local => EngineConfig { + // TODO: Update with real values + r#type: "local_sqlite".to_string(), + image: "docker.io/library/postgres:11".to_string(), + engine: "sqlite".to_string(), + username: "sqlite".to_string(), + password: "sqlite".to_string(), + database_name: "sqlite".to_string(), + port: "5432/tcp".to_string(), + env: Some(vec!["SQLITE_PASSWORD=sqlite".to_string()]), + is_ready_cmd: vec![], + // is_ready_cmd: vec![ + // "/bin/sh".to_string(), + // "-c".to_string(), + // "pg_isready | grep 'accepting connections'".to_string(), + // ], + }, } } diff --git a/common/src/database.rs b/common/src/database.rs index 0f657d65f8..4dcc0d13db 100644 --- a/common/src/database.rs +++ b/common/src/database.rs @@ -12,6 +12,7 @@ use utoipa::ToSchema; pub enum Type { AwsRds(AwsRdsEngine), Shared(SharedEngine), + Local, } #[derive(Clone, Debug, Deserialize, Display, Serialize, Eq, PartialEq)] @@ -38,6 +39,7 @@ impl Display for Type { match self { Type::AwsRds(rds_type) => write!(f, "aws_rds::{rds_type}"), Type::Shared(shared_type) => write!(f, "shared::{shared_type}"), + Type::Local => write!(f, "local"), } } } diff --git a/deployer/src/persistence/resource/database.rs b/deployer/src/persistence/resource/database.rs index 1d2a381df6..36216a9bd0 100644 --- a/deployer/src/persistence/resource/database.rs +++ b/deployer/src/persistence/resource/database.rs @@ -6,6 +6,7 @@ use strum::{Display, EnumString}; pub enum Type { AwsRds(AwsRdsType), Shared(SharedType), + Local, } #[derive(Clone, Copy, Debug, Display, EnumString, Eq, PartialEq)] @@ -28,6 +29,7 @@ impl From for shuttle_common::database::Type { match r#type { Type::AwsRds(rds_type) => Self::AwsRds(rds_type.into()), Type::Shared(shared_type) => Self::Shared(shared_type.into()), + Type::Local => Self::Local, } } } @@ -56,6 +58,7 @@ impl From for Type { match r#type { shuttle_common::database::Type::AwsRds(rds_type) => Self::AwsRds(rds_type.into()), shuttle_common::database::Type::Shared(shared_type) => Self::Shared(shared_type.into()), + shuttle_common::database::Type::Local => Self::Local, } } } @@ -84,6 +87,7 @@ impl Display for Type { match self { Type::AwsRds(rds_type) => write!(f, "aws_rds::{rds_type}"), Type::Shared(shared_type) => write!(f, "shared::{shared_type}"), + Type::Local => write!(f, "local"), } } } diff --git a/provisioner/src/lib.rs b/provisioner/src/lib.rs index cfa9ebdc8a..4f1abaf0c7 100644 --- a/provisioner/src/lib.rs +++ b/provisioner/src/lib.rs @@ -8,6 +8,7 @@ use mongodb::{bson::doc, options::ClientOptions}; use rand::Rng; use shuttle_common::claims::{Claim, Scope}; pub use shuttle_proto::provisioner::provisioner_server::ProvisionerServer; +use shuttle_proto::provisioner::Local; use shuttle_proto::provisioner::{ aws_rds, database_request::DbType, shared, AwsRds, DatabaseRequest, DatabaseResponse, Shared, }; @@ -425,6 +426,7 @@ impl Provisioner for MyProvisioner { self.request_aws_rds(&request.project_name, engine.expect("oneof to be set")) .await? } + DbType::Local(Local {}) => todo!(), }; Ok(Response::new(reply)) @@ -449,6 +451,7 @@ impl Provisioner for MyProvisioner { self.delete_aws_rds(&request.project_name, engine.expect("oneof to be set")) .await? } + DbType::Local(Local {}) => todo!(), }; Ok(Response::new(reply)) diff --git a/resources/sqlite/Cargo.toml b/resources/sqlite/Cargo.toml new file mode 100644 index 0000000000..4dddfe9fd7 --- /dev/null +++ b/resources/sqlite/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sqlite" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +async-trait = "0.1.68" +serde = "1.0.163" +shuttle-service = "0.16.0" +shuttle-common = { version = "0.16.0", path = "../../common" } diff --git a/resources/sqlite/src/lib.rs b/resources/sqlite/src/lib.rs new file mode 100644 index 0000000000..82b792998f --- /dev/null +++ b/resources/sqlite/src/lib.rs @@ -0,0 +1,49 @@ +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use shuttle_service::{database, Factory, ResourceBuilder, Type}; + +// Builder struct +#[derive(Serialize)] +pub struct SQLite {} + +// Resource struct +#[derive(Deserialize, Serialize, Clone)] +pub struct SQLiteInstance {} + +#[async_trait] +impl ResourceBuilder for SQLite { + const TYPE: Type = Type::Database(database::Type::Local); + + type Config = Self; + + type Output = SQLiteInstance; + + fn new() -> Self { + Self {} + } + + fn config(&self) -> &Self::Config { + &self + } + + async fn output( + self, + factory: &mut dyn Factory, + ) -> Result { + Ok(SQLiteInstance {}) + } + + async fn build(build_data: &Self::Output) -> Result { + Ok(build_data.clone()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + // + } +}