Compare commits

..

4 Commits

Author SHA1 Message Date
76f7bbc9e2 tsk-61: Updating readme 2025-10-20 12:16:43 -04:00
34687dda7d tsk-61: Adding changes to make feature available 2025-10-20 12:16:31 -04:00
6c83e566bf tsk-61: Filename change 2025-10-20 12:12:19 -04:00
b74c0fc3b0 tsk-61: Added env variable 2025-10-20 12:11:52 -04:00
15 changed files with 152 additions and 154 deletions

View File

@@ -76,7 +76,6 @@ jobs:
SECRET_KEY: ${{ secrets.TOKEN_SECRET_KEY }}
# Make SSH agent available if tests fetch private dependencies
SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK }}
ENABLE_REGISTRATION: 'TRUE'
run: |
mkdir -p ~/.ssh
echo "${{ secrets.MYREPO_TOKEN }}" > ~/.ssh/icarus_models_deploy_key

2
Cargo.lock generated
View File

@@ -748,7 +748,7 @@ dependencies = [
[[package]]
name = "icarus_auth"
version = "0.6.5"
version = "0.6.2"
dependencies = [
"argon2",
"axum",

View File

@@ -1,6 +1,6 @@
[package]
name = "icarus_auth"
version = "0.6.5"
version = "0.6.2"
edition = "2024"
rust-version = "1.90"

View File

@@ -2,13 +2,6 @@ A auth web API services for the Icarus project.
# Getting Started
Install the `sqlx` tool to use migrations.
```
cargo install sqlx-cli
```
This will be used to scaffold development for local environments.
The easiest way to get started is through docker. This assumes that docker is already installed
on your system. Copy the `.env.docker.sample` as `.env`. Most of the data in the env file doesn't
need to be modified. The `SECRET_KEY` variable should be changed since it will be used for token
@@ -40,4 +33,4 @@ docker system prune -a
```
To view the OpenAPI spec, run the project and access `/swagger-ui`. If running through docker,
the url would be something like `http://localhost:8001/swagger-ui`.
the url would be something like `http://localhost:8000/swagger-ui`.

View File

@@ -23,7 +23,6 @@ CREATE TABLE IF NOT EXISTS "salt" (
CREATE TABLE IF NOT EXISTS "passphrase" (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username TEXT NOT NULL,
passphrase TEXT NOT NULL,
date_created TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

View File

@@ -1,2 +1,2 @@
-- Add migration script here
INSERT INTO "passphrase" (id, username, passphrase) VALUES('22f9c775-cce9-457a-a147-9dafbb801f61', 'service', 'iUOo1fxshf3y1tUGn1yU8l9raPApHCdinW0VdCHdRFEjqhR3Bf02aZzsKbLtaDFH');
INSERT INTO "passphrase" (id, passphrase) VALUES('22f9c775-cce9-457a-a147-9dafbb801f61', 'iUOo1fxshf3y1tUGn1yU8l9raPApHCdinW0VdCHdRFEjqhR3Bf02aZzsKbLtaDFH');

27
run_migrations.txt Normal file
View File

@@ -0,0 +1,27 @@
TODO: At some point, move this somewhere that is appropriate
# Make sure role has CREATEDB
ALTER ROLE username_that_needs_permission CREATEDB;
# Install migrations
cargo install sqlx-cli
# Make sure to populate DATABASE_URL with correct value.
# By default, the DATABASE_URL found in .env file will be used
export DATABASE_URL="postgres://icarus_op_test:password@localhost/icarus_auth_test"
# init
sqlx migrate add init_migration
sqlx migrate run
# Create
sqlx database create
# Drop
sqlx database drop
# setup
sqlx database setup
# Reset
sqlx database reset

View File

@@ -59,6 +59,10 @@ pub mod endpoint {
use super::request;
use super::response;
// TODO: At some point, get the username from the DB
// Name of service username when returning a login result
pub const SERVICE_USERNAME: &str = "service";
async fn not_found(message: &str) -> (StatusCode, Json<response::Response>) {
(
StatusCode::NOT_FOUND,
@@ -114,7 +118,7 @@ pub mod endpoint {
}),
)
} else {
return not_found("Could not verify token").await;
return not_found("Could not verify password").await;
}
} else {
return not_found("Error Hashing").await;
@@ -150,7 +154,7 @@ pub mod endpoint {
let mut response = response::service_login::Response::default();
match repo::service::valid_passphrase(&pool, &payload.passphrase).await {
Ok((id, username, _date_created)) => {
Ok((id, _passphrase, _date_created)) => {
let key = icarus_envy::environment::get_secret_key().await.value;
let (token_literal, duration) =
token_stuff::create_service_token(&key, &id).unwrap();
@@ -158,7 +162,7 @@ pub mod endpoint {
if token_stuff::verify_token(&key, &token_literal) {
let login_result = icarus_models::login_result::LoginResult {
id,
username,
username: String::from(SERVICE_USERNAME),
token: token_literal,
token_type: String::from(icarus_models::token::TOKEN_TYPE),
expiration: duration,
@@ -212,15 +216,15 @@ pub mod endpoint {
// Get passphrase record with id
match token_stuff::extract_id_from_token(&key, &payload.access_token) {
Ok(id) => match repo::service::get_passphrase(&pool, &id).await {
Ok((username, _, _)) => {
match token_stuff::create_service_refresh_token(&key, &id) {
Ok((returned_id, _, _)) => {
match token_stuff::create_service_refresh_token(&key, &returned_id) {
Ok((access_token, exp_dur)) => {
let login_result = icarus_models::login_result::LoginResult {
id,
id: returned_id,
token: access_token,
expiration: exp_dur,
token_type: String::from(icarus_models::token::TOKEN_TYPE),
username,
username: String::from(SERVICE_USERNAME),
};
response.message = String::from("Successful");
response.data.push(login_result);

View File

@@ -52,21 +52,7 @@ pub async fn register_user(
axum::Extension(pool): axum::Extension<sqlx::PgPool>,
Json(payload): Json<request::Request>,
) -> (StatusCode, Json<response::Response>) {
let registration_enabled = match is_registration_enabled().await {
Ok(value) => value,
Err(err) => {
eprintln!("Error: {err:?}");
return (
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
Json(response::Response {
message: String::from("Registration check failed"),
data: Vec::new(),
}),
);
}
};
if registration_enabled {
if is_registration_enabled() {
let mut user = icarus_models::user::User {
username: payload.username.clone(),
password: payload.password.clone(),
@@ -131,29 +117,9 @@ pub async fn register_user(
),
}
} else {
(
axum::http::StatusCode::NOT_ACCEPTABLE,
Json(response::Response {
message: String::from("Registration is not enabled"),
data: Vec::new(),
}),
)
}
}
/// Checks to see if registration is enabled
async fn is_registration_enabled() -> Result<bool, std::io::Error> {
let key = String::from("ENABLE_REGISTRATION");
let var = icarus_envy::environment::get_env(&key).await;
let parsed_value = var.value.to_uppercase();
if parsed_value == "TRUE" {
Ok(true)
} else if parsed_value == "FALSE" {
Ok(false)
} else {
Err(std::io::Error::other(
"Could not determine value of ENABLE_REGISTRATION",
))
(axum::http::StatusCode::NOT_ACCEPTABLE, Json(response::Response{
message: String::from("Registration is not enabled"),
data:: Vec::new()
}))
}
}

View File

@@ -1,20 +0,0 @@
use sqlx::postgres::PgPoolOptions;
pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
let database_url = icarus_envy::environment::get_db_url().await.value;
println!("Database url: {database_url}");
PgPoolOptions::new()
.max_connections(super::connection_settings::MAXCONN)
.connect(&database_url)
.await
}
pub async fn migrations(pool: &sqlx::PgPool) {
// Run migrations using the sqlx::migrate! macro
// Assumes your migrations are in a ./migrations folder relative to Cargo.toml
sqlx::migrate!("./migrations")
.run(pool)
.await
.expect("Failed to run migrations");
}

View File

@@ -1,5 +0,0 @@
pub mod init;
mod connection_settings {
pub const MAXCONN: u32 = 5;
}

36
src/lib.rs Normal file
View File

@@ -0,0 +1,36 @@
// TODO: Get rid of this file and place the code in more appropriate places
pub mod callers;
pub mod config;
pub mod hashing;
pub mod repo;
pub mod token_stuff;
mod connection_settings {
pub const MAXCONN: u32 = 5;
}
pub mod db {
use sqlx::postgres::PgPoolOptions;
use crate::connection_settings;
pub async fn create_pool() -> Result<sqlx::PgPool, sqlx::Error> {
let database_url = icarus_envy::environment::get_db_url().await.value;
println!("Database url: {database_url}");
PgPoolOptions::new()
.max_connections(connection_settings::MAXCONN)
.connect(&database_url)
.await
}
pub async fn migrations(pool: &sqlx::PgPool) {
// Run migrations using the sqlx::migrate! macro
// Assumes your migrations are in a ./migrations folder relative to Cargo.toml
sqlx::migrate!("./migrations")
.run(pool)
.await
.expect("Failed to run migrations");
}
}

View File

@@ -1,9 +1,5 @@
pub mod callers;
pub mod config;
pub mod db;
pub mod hashing;
pub mod repo;
pub mod token_stuff;
use icarus_auth::callers;
use icarus_auth::config;
#[tokio::main]
async fn main() {
@@ -25,7 +21,7 @@ mod init {
};
use utoipa::OpenApi;
use super::callers;
use crate::callers;
use callers::common as common_callers;
use callers::login as login_caller;
use callers::register as register_caller;
@@ -128,11 +124,11 @@ mod init {
}
pub async fn app() -> Router {
let pool = super::db::init::create_pool()
let pool = icarus_auth::db::create_pool()
.await
.expect("Failed to create pool");
super::db::init::migrations(&pool).await;
icarus_auth::db::migrations(&pool).await;
routes()
.await
@@ -220,8 +216,8 @@ mod tests {
}
}
fn get_test_register_request() -> callers::register::request::Request {
callers::register::request::Request {
fn get_test_register_request() -> icarus_auth::callers::register::request::Request {
icarus_auth::callers::register::request::Request {
username: String::from("somethingsss"),
password: String::from("Raindown!"),
email: String::from("dev@null.com"),
@@ -231,7 +227,9 @@ mod tests {
}
}
fn get_test_register_payload(usr: &callers::register::request::Request) -> serde_json::Value {
fn get_test_register_payload(
usr: &icarus_auth::callers::register::request::Request,
) -> serde_json::Value {
json!({
"username": &usr.username,
"password": &usr.password,
@@ -247,7 +245,7 @@ mod tests {
pub async fn register(
app: &axum::Router,
usr: &super::callers::register::request::Request,
usr: &icarus_auth::callers::register::request::Request,
) -> Result<axum::response::Response, std::convert::Infallible> {
let payload = super::get_test_register_payload(&usr);
let req = axum::http::Request::builder()
@@ -300,7 +298,7 @@ mod tests {
let pool = db_mgr::connect_to_db(&db_name).await.unwrap();
db::init::migrations(&pool).await;
icarus_auth::db::migrations(&pool).await;
let app = init::routes().await.layer(axum::Extension(pool));
@@ -357,7 +355,7 @@ mod tests {
let pool = db_mgr::connect_to_db(&db_name).await.unwrap();
db::init::migrations(&pool).await;
icarus_auth::db::migrations(&pool).await;
let app = init::routes().await.layer(axum::Extension(pool));
@@ -445,7 +443,7 @@ mod tests {
let pool = db_mgr::connect_to_db(&db_name).await.unwrap();
db::init::migrations(&pool).await;
icarus_auth::db::migrations(&pool).await;
let app = init::routes().await.layer(axum::Extension(pool));
let passphrase =
@@ -499,13 +497,13 @@ mod tests {
let pool = db_mgr::connect_to_db(&db_name).await.unwrap();
db::init::migrations(&pool).await;
icarus_auth::db::migrations(&pool).await;
let app = init::routes().await.layer(axum::Extension(pool));
let id = uuid::Uuid::parse_str("22f9c775-cce9-457a-a147-9dafbb801f61").unwrap();
let key = icarus_envy::environment::get_secret_key().await.value;
match token_stuff::create_service_token(&key, &id) {
match icarus_auth::token_stuff::create_service_token(&key, &id) {
Ok((token, _expire)) => {
let payload = serde_json::json!({
"access_token": token

View File

@@ -1,5 +1,3 @@
pub mod service;
pub mod user {
use sqlx::Row;
@@ -197,3 +195,56 @@ pub mod salt {
}
}
}
pub mod service {
use sqlx::Row;
pub async fn valid_passphrase(
pool: &sqlx::PgPool,
passphrase: &String,
) -> Result<(uuid::Uuid, String, time::OffsetDateTime), sqlx::Error> {
let result = sqlx::query(
r#"
SELECT * FROM "passphrase" WHERE passphrase = $1
"#,
)
.bind(passphrase)
.fetch_one(pool)
.await;
match result {
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
let passphrase: String = row.try_get("passphrase")?;
let date_created: Option<time::OffsetDateTime> = row.try_get("date_created")?;
Ok((id, passphrase, date_created.unwrap()))
}
Err(err) => Err(err),
}
}
pub async fn get_passphrase(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<(uuid::Uuid, String, time::OffsetDateTime), sqlx::Error> {
let result = sqlx::query(
r#"
SELECT * FROM "passphrase" WHERE id = $1;
"#,
)
.bind(id)
.fetch_one(pool)
.await;
match result {
Ok(row) => {
let returned_id: uuid::Uuid = row.try_get("id")?;
let passphrase: String = row.try_get("passphrase")?;
let date_created: time::OffsetDateTime = row.try_get("date_created")?;
Ok((returned_id, passphrase, date_created))
}
Err(err) => Err(err),
}
}
}

View File

@@ -1,50 +0,0 @@
use sqlx::Row;
pub async fn valid_passphrase(
pool: &sqlx::PgPool,
passphrase: &String,
) -> Result<(uuid::Uuid, String, time::OffsetDateTime), sqlx::Error> {
let result = sqlx::query(
r#"
SELECT id, username, date_created FROM "passphrase" WHERE passphrase = $1
"#,
)
.bind(passphrase)
.fetch_one(pool)
.await;
match result {
Ok(row) => {
let id: uuid::Uuid = row.try_get("id")?;
let username: String = row.try_get("username")?;
let date_created: Option<time::OffsetDateTime> = row.try_get("date_created")?;
Ok((id, username, date_created.unwrap()))
}
Err(err) => Err(err),
}
}
pub async fn get_passphrase(
pool: &sqlx::PgPool,
id: &uuid::Uuid,
) -> Result<(String, String, time::OffsetDateTime), sqlx::Error> {
let result = sqlx::query(
r#"
SELECT username, passphrase, date_created FROM "passphrase" WHERE id = $1;
"#,
)
.bind(id)
.fetch_one(pool)
.await;
match result {
Ok(row) => {
let username: String = row.try_get("username")?;
let passphrase: String = row.try_get("passphrase")?;
let date_created: time::OffsetDateTime = row.try_get("date_created")?;
Ok((username, passphrase, date_created))
}
Err(err) => Err(err),
}
}