Compare commits
8 Commits
v0.3.4
...
v0.3.4-dev
Author | SHA1 | Date | |
---|---|---|---|
480a428e8b | |||
02697b2fd9 | |||
d4faa7976e | |||
ed77cab700 | |||
2c30abb5c6 | |||
1817ab01d6 | |||
31be156be3 | |||
fc6b66f2e6 |
@@ -5,6 +5,8 @@ pkg/
|
|||||||
# Ignore git directory
|
# Ignore git directory
|
||||||
.git/
|
.git/
|
||||||
|
|
||||||
|
.gitea/
|
||||||
|
|
||||||
# Ignore environment files (configure via docker-compose instead)
|
# Ignore environment files (configure via docker-compose instead)
|
||||||
.env*
|
.env*
|
||||||
|
|
||||||
|
6
.env.docker.sample
Normal file
6
.env.docker.sample
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
SECRET_KEY=refero34o8rfhfjn983thf39fhc943rf923n3h
|
||||||
|
POSTGRES_AUTH_USER=icarus_op
|
||||||
|
POSTGRES_AUTH_PASSWORD=password
|
||||||
|
POSTGRES_AUTH_DB=icarus_auth_db
|
||||||
|
POSTGRES_AUTH_HOST=auth_db
|
||||||
|
DATABASE_URL=postgresql://${POSTGRES_AUTH_USER}:${POSTGRES_AUTH_PASSWORD}@${POSTGRES_AUTH_HOST}:5432/${POSTGRES_AUTH_DB}
|
@@ -1,2 +1,6 @@
|
|||||||
DATABASE_URL=postgres://username:password@localhost/database_name
|
|
||||||
SECRET_KEY=refero34o8rfhfjn983thf39fhc943rf923n3h
|
SECRET_KEY=refero34o8rfhfjn983thf39fhc943rf923n3h
|
||||||
|
POSTGRES_AUTH_USER=icarus_op_test
|
||||||
|
POSTGRES_AUTH_PASSWORD=password
|
||||||
|
POSTGRES_AUTH_DB=icarus_auth_test_db
|
||||||
|
POSTGRES_AUTH_HOST=localhost
|
||||||
|
DATABASE_URL=postgresql://${POSTGRES_AUTH_USER}:${POSTGRES_AUTH_PASSWORD}@${POSTGRES_AUTH_HOST}:5432/${POSTGRES_AUTH_DB}
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
|
||||||
.env
|
.env
|
||||||
|
2527
Cargo.lock
generated
Normal file
2527
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -13,13 +13,13 @@ tracing-subscriber = { version = "0.3.19" }
|
|||||||
tower = { version = "0.5.2" }
|
tower = { version = "0.5.2" }
|
||||||
hyper = { version = "1.6.0" }
|
hyper = { version = "1.6.0" }
|
||||||
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] }
|
sqlx = { version = "0.8.3", features = ["postgres", "runtime-tokio-native-tls", "time", "uuid"] }
|
||||||
dotenvy = { version = "0.15.7" }
|
|
||||||
uuid = { version = "1.16.0", features = ["v4", "serde"] }
|
uuid = { version = "1.16.0", features = ["v4", "serde"] }
|
||||||
argon2 = { version = "0.5.3", features = ["std"] } # Use the latest 0.5.x version
|
argon2 = { version = "0.5.3", features = ["std"] } # Use the latest 0.5.x version
|
||||||
rand = { version = "0.9" }
|
rand = { version = "0.9" }
|
||||||
time = { version = "0.3.41", features = ["macros", "serde"] }
|
time = { version = "0.3.41", features = ["macros", "serde"] }
|
||||||
josekit = { version = "0.10.1" }
|
josekit = { version = "0.10.1" }
|
||||||
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.4.1" }
|
icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.4.3" }
|
||||||
|
icarus_envy = { git = "ssh://git@git.kundeng.us/phoenix/icarus_envy.git", tag = "v0.2.0-devel-dbe4dc67cb-950" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
http-body-util = { version = "0.1.3" }
|
http-body-util = { version = "0.1.3" }
|
||||||
|
@@ -34,8 +34,6 @@ RUN --mount=type=ssh mkdir src && \
|
|||||||
# Copy the actual source code
|
# Copy the actual source code
|
||||||
COPY src ./src
|
COPY src ./src
|
||||||
# If you have other directories like `templates` or `static`, copy them too
|
# If you have other directories like `templates` or `static`, copy them too
|
||||||
# COPY templates ./templates
|
|
||||||
# COPY static ./static
|
|
||||||
COPY .env ./.env
|
COPY .env ./.env
|
||||||
COPY migrations ./migrations
|
COPY migrations ./migrations
|
||||||
|
|
||||||
@@ -64,8 +62,6 @@ COPY --from=builder /usr/src/app/target/release/icarus_auth .
|
|||||||
# It's generally better to configure via environment variables in Docker though
|
# It's generally better to configure via environment variables in Docker though
|
||||||
COPY --from=builder /usr/src/app/.env .
|
COPY --from=builder /usr/src/app/.env .
|
||||||
COPY --from=builder /usr/src/app/migrations ./migrations
|
COPY --from=builder /usr/src/app/migrations ./migrations
|
||||||
# COPY --from=builder /usr/src/app/templates ./templates
|
|
||||||
# COPY --from=builder /usr/src/app/static ./static
|
|
||||||
|
|
||||||
# Expose the port your Axum app listens on (e.g., 3000 or 8000)
|
# Expose the port your Axum app listens on (e.g., 3000 or 8000)
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
Copy the `.env.sample` file to `.env` and ensure that the variables are populated. This project
|
||||||
|
can be used with regular hosting or with docker. For the sake of getting up to speed quickly,
|
||||||
|
Docker will be covered. Make sure docker is running and your ssh identity has been loaded.
|
||||||
|
|
||||||
|
Build image
|
||||||
|
```
|
||||||
|
docker compose build
|
||||||
|
```
|
||||||
|
|
||||||
|
Start images
|
||||||
|
```
|
||||||
|
docker compose up -d --force-recreate
|
||||||
|
```
|
||||||
|
|
||||||
|
Bring it down
|
||||||
|
```
|
||||||
|
docker compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
Pruning
|
||||||
|
```
|
||||||
|
docker system prune -a
|
||||||
|
```
|
@@ -2,43 +2,34 @@ version: '3.8' # Use a recent version
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
# Your Rust Application Service
|
# Your Rust Application Service
|
||||||
app:
|
auth_api:
|
||||||
build: . # Tells docker-compose to build the Dockerfile in the current directory
|
build: # Tells docker-compose to build the Dockerfile in the current directory
|
||||||
|
context: .
|
||||||
|
ssh: ["default"] # Uses host's SSH agent
|
||||||
container_name: icarus_auth # Optional: Give the container a specific name
|
container_name: icarus_auth # Optional: Give the container a specific name
|
||||||
ports:
|
ports:
|
||||||
# Map host port 8000 to container port 3000 (adjust as needed)
|
# Map host port 8000 to container port 3000 (adjust as needed)
|
||||||
# Format: "HOST_PORT:CONTAINER_PORT"
|
|
||||||
- "8000:3000"
|
- "8000:3000"
|
||||||
environment:
|
env_file:
|
||||||
# Pass environment variables to your Rust application
|
- .env
|
||||||
# RUST_LOG: info # Example: Set log level
|
|
||||||
# IMPORTANT: Configure DATABASE_URL to connect to the 'db' service
|
|
||||||
# The hostname 'db' matches the service name defined below.
|
|
||||||
DATABASE_URL: postgresql://icarus_op:password@db:5432/icarus_auth
|
|
||||||
# Add any other environment variables your app needs
|
|
||||||
# APP_HOST: 0.0.0.0
|
|
||||||
# APP_PORT: 3000
|
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
auth_db:
|
||||||
condition: service_healthy # Wait for the DB to be healthy before starting the app
|
condition: service_healthy # Wait for the DB to be healthy before starting the app
|
||||||
restart: unless-stopped # Optional: Restart policy
|
restart: unless-stopped # Optional: Restart policy
|
||||||
|
|
||||||
# PostgreSQL Database Service
|
# PostgreSQL Database Service
|
||||||
db:
|
auth_db:
|
||||||
image: postgres:17.4-alpine # Use an official Postgres image (Alpine variant is smaller)
|
image: postgres:17.4-alpine # Use an official Postgres image (Alpine variant is smaller)
|
||||||
container_name: icarus_auth_db # Optional: Give the container a specific name
|
container_name: icarus_auth_db # Optional: Give the container a specific name
|
||||||
environment:
|
environment:
|
||||||
# These MUST match the user, password, and database name in the DATABASE_URL above
|
# These MUST match the user, password, and database name in the DATABASE_URL above
|
||||||
POSTGRES_USER: icarus_op
|
POSTGRES_USER: ${POSTGRES_AUTH_USER:-icarus_op}
|
||||||
POSTGRES_PASSWORD: password
|
POSTGRES_PASSWORD: ${POSTGRES_AUTH_PASSWORD:-password}
|
||||||
POSTGRES_DB: icarus_auth
|
POSTGRES_DB: ${POSTGRES_AUTH_DB:-icarus_auth_db}
|
||||||
volumes:
|
volumes:
|
||||||
# Persist database data using a named volume
|
# Persist database data using a named volume
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
ports: []
|
ports: []
|
||||||
# Optional: Expose port 5432 ONLY if you need to connect directly from your host machine (e.g., for debugging)
|
|
||||||
# - "5432:5432"
|
|
||||||
# pass:
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
# Checks if Postgres is ready to accept connections
|
# Checks if Postgres is ready to accept connections
|
||||||
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"]
|
||||||
|
@@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
# Docker stuff
|
|
||||||
#Build app
|
|
||||||
docker-compose build --ssh default app
|
|
||||||
|
|
||||||
# Rebuild and bring up
|
|
||||||
docker-compose up -d --force-recreate app
|
|
||||||
|
|
||||||
# Bring it down
|
|
||||||
docker-compose down -v
|
|
||||||
|
|
||||||
# Pruning
|
|
||||||
docker system prune -a
|
|
@@ -1,3 +1,5 @@
|
|||||||
|
TODO: At some point, move this somewhere that is appropriate
|
||||||
|
|
||||||
# Make sure role has CREATEDB
|
# Make sure role has CREATEDB
|
||||||
ALTER ROLE username_that_needs_permission CREATEDB;
|
ALTER ROLE username_that_needs_permission CREATEDB;
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ pub mod endpoint {
|
|||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
if hashing::verify_password(&payload.password, user.password.clone()).unwrap() {
|
if hashing::verify_password(&payload.password, user.password.clone()).unwrap() {
|
||||||
// Create token
|
// Create token
|
||||||
let key = token_stuff::get_key().unwrap();
|
let key = icarus_envy::environment::get_secret_key().await;
|
||||||
let (token_literal, duration) = token_stuff::create_token(&key).unwrap();
|
let (token_literal, duration) = token_stuff::create_token(&key).unwrap();
|
||||||
|
|
||||||
if token_stuff::verify_token(&key, &token_literal) {
|
if token_stuff::verify_token(&key, &token_literal) {
|
||||||
|
20
src/lib.rs
20
src/lib.rs
@@ -4,14 +4,6 @@ pub mod hashing;
|
|||||||
pub mod repo;
|
pub mod repo;
|
||||||
pub mod token_stuff;
|
pub mod token_stuff;
|
||||||
|
|
||||||
pub mod keys {
|
|
||||||
pub const DBURL: &str = "DATABASE_URL";
|
|
||||||
|
|
||||||
pub mod error {
|
|
||||||
pub const ERROR: &str = "DATABASE_URL must be set in .env";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod connection_settings {
|
mod connection_settings {
|
||||||
pub const MAXCONN: u32 = 5;
|
pub const MAXCONN: u32 = 5;
|
||||||
}
|
}
|
||||||
@@ -19,12 +11,11 @@ mod connection_settings {
|
|||||||
pub mod db {
|
pub mod db {
|
||||||
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use crate::{connection_settings, keys};
|
use crate::connection_settings;
|
||||||
|
|
||||||
pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
||||||
let database_url = get_db_url().await;
|
let database_url = icarus_envy::environment::get_db_url().await;
|
||||||
println!("Database url: {:?}", database_url);
|
println!("Database url: {:?}", database_url);
|
||||||
|
|
||||||
PgPoolOptions::new()
|
PgPoolOptions::new()
|
||||||
@@ -33,13 +24,6 @@ pub mod db {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_db_url() -> String {
|
|
||||||
#[cfg(debug_assertions)] // Example: Only load .env in debug builds
|
|
||||||
dotenvy::dotenv().ok();
|
|
||||||
|
|
||||||
env::var(keys::DBURL).expect(keys::error::ERROR)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn migrations(pool: &sqlx::PgPool) {
|
pub async fn migrations(pool: &sqlx::PgPool) {
|
||||||
// Run migrations using the sqlx::migrate! macro
|
// Run migrations using the sqlx::migrate! macro
|
||||||
// Assumes your migrations are in a ./migrations folder relative to Cargo.toml
|
// Assumes your migrations are in a ./migrations folder relative to Cargo.toml
|
||||||
|
45
src/main.rs
45
src/main.rs
@@ -69,24 +69,23 @@ mod tests {
|
|||||||
mod db_mgr {
|
mod db_mgr {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use icarus_auth::keys;
|
|
||||||
|
|
||||||
pub const LIMIT: usize = 6;
|
pub const LIMIT: usize = 6;
|
||||||
|
|
||||||
pub async fn get_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
pub async fn get_pool() -> Result<sqlx::PgPool, sqlx::Error> {
|
||||||
let tm_db_url = std::env::var(keys::DBURL).expect("DATABASE_URL must be present");
|
let tm_db_url = icarus_envy::environment::get_db_url().await;
|
||||||
let tm_options = sqlx::postgres::PgConnectOptions::from_str(&tm_db_url).unwrap();
|
let tm_options = sqlx::postgres::PgConnectOptions::from_str(&tm_db_url).unwrap();
|
||||||
sqlx::PgPool::connect_with(tm_options).await
|
sqlx::PgPool::connect_with(tm_options).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate_db_name() -> String {
|
pub async fn generate_db_name() -> String {
|
||||||
let db_name =
|
let db_name = get_database_name().await.unwrap()
|
||||||
get_database_name().unwrap() + &"_" + &uuid::Uuid::new_v4().to_string()[..LIMIT];
|
+ &"_"
|
||||||
|
+ &uuid::Uuid::new_v4().to_string()[..LIMIT];
|
||||||
db_name
|
db_name
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn connect_to_db(db_name: &str) -> Result<sqlx::PgPool, sqlx::Error> {
|
pub async fn connect_to_db(db_name: &str) -> Result<sqlx::PgPool, sqlx::Error> {
|
||||||
let db_url = std::env::var(keys::DBURL).expect("DATABASE_URL must be set for tests");
|
let db_url = icarus_envy::environment::get_db_url().await;
|
||||||
let options = sqlx::postgres::PgConnectOptions::from_str(&db_url)?.database(db_name);
|
let options = sqlx::postgres::PgConnectOptions::from_str(&db_url)?.database(db_name);
|
||||||
sqlx::PgPool::connect_with(options).await
|
sqlx::PgPool::connect_with(options).await
|
||||||
}
|
}
|
||||||
@@ -112,29 +111,21 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_database_name() -> Result<String, Box<dyn std::error::Error>> {
|
pub async fn get_database_name() -> Result<String, Box<dyn std::error::Error>> {
|
||||||
dotenvy::dotenv().ok(); // Load .env file if it exists
|
let database_url = icarus_envy::environment::get_db_url().await;
|
||||||
|
|
||||||
match std::env::var(keys::DBURL) {
|
let parsed_url = url::Url::parse(&database_url)?;
|
||||||
Ok(database_url) => {
|
if parsed_url.scheme() == "postgres" || parsed_url.scheme() == "postgresql" {
|
||||||
let parsed_url = url::Url::parse(&database_url)?;
|
match parsed_url
|
||||||
if parsed_url.scheme() == "postgres" || parsed_url.scheme() == "postgresql" {
|
.path_segments()
|
||||||
match parsed_url
|
.and_then(|segments| segments.last().map(|s| s.to_string()))
|
||||||
.path_segments()
|
{
|
||||||
.and_then(|segments| segments.last().map(|s| s.to_string()))
|
Some(sss) => Ok(sss),
|
||||||
{
|
None => Err("Error parsing".into()),
|
||||||
Some(sss) => Ok(sss),
|
|
||||||
None => Err("Error parsing".into()),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Handle other database types if needed
|
|
||||||
Err("Error parsing".into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
// DATABASE_URL environment variable not found
|
|
||||||
Err("Error parsing".into())
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Handle other database types if needed
|
||||||
|
Err("Error parsing".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,12 +12,6 @@ pub const MESSAGE: &str = "Something random";
|
|||||||
pub const ISSUER: &str = "icarus_auth";
|
pub const ISSUER: &str = "icarus_auth";
|
||||||
pub const AUDIENCE: &str = "icarus";
|
pub const AUDIENCE: &str = "icarus";
|
||||||
|
|
||||||
pub fn get_key() -> Result<String, dotenvy::Error> {
|
|
||||||
dotenvy::dotenv().ok();
|
|
||||||
let key = std::env::var(KEY_ENV).expect("SECRET_KEY_NOT_FOUND");
|
|
||||||
Ok(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_issued() -> time::Result<time::OffsetDateTime> {
|
pub fn get_issued() -> time::Result<time::OffsetDateTime> {
|
||||||
Ok(time::OffsetDateTime::now_utc())
|
Ok(time::OffsetDateTime::now_utc())
|
||||||
}
|
}
|
||||||
@@ -51,7 +45,10 @@ pub fn create_token(provided_key: &String) -> Result<(String, i64), josekit::Jos
|
|||||||
payload.set_expires_at(&util::time_to_std_time(&expire).unwrap());
|
payload.set_expires_at(&util::time_to_std_time(&expire).unwrap());
|
||||||
|
|
||||||
let key: String = if provided_key.is_empty() {
|
let key: String = if provided_key.is_empty() {
|
||||||
get_key().unwrap()
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
|
||||||
|
// Block on the async function to get the result
|
||||||
|
rt.block_on(icarus_envy::environment::get_secret_key())
|
||||||
} else {
|
} else {
|
||||||
provided_key.to_owned()
|
provided_key.to_owned()
|
||||||
};
|
};
|
||||||
@@ -82,7 +79,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tokenize() {
|
fn test_tokenize() {
|
||||||
let special_key = get_key().unwrap();
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
|
let special_key = rt.block_on(icarus_envy::environment::get_secret_key());
|
||||||
match create_token(&special_key) {
|
match create_token(&special_key) {
|
||||||
Ok((token, _duration)) => {
|
Ok((token, _duration)) => {
|
||||||
let result = verify_token(&special_key, &token);
|
let result = verify_token(&special_key, &token);
|
||||||
|
Reference in New Issue
Block a user