Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,405 changes: 683 additions & 722 deletions Cargo.lock

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "openbridge"
name = "bridge"
version = "0.1.0"
edition = "2024"

[[bin]]
name = "openbridge"
name = "bridge"
path = "./src/main.rs"

[workspace]
Expand Down Expand Up @@ -33,11 +33,11 @@ time = { version = "0.3.36", features = ["serde"] }
parking_lot = "~0.12"
num-bigint = "~0.4"
# Web deps
actix-web = { version = "~4.11", features = ["rustls-0_23", "cookies"] }
actix-web = { version = "~4.12", features = ["rustls-0_23", "cookies"] }
actix-web-httpauth = "0.8"
actix-files = "0.6.6"
tera = { version = "1.20.0", features= ["builtins"] }
reqwest = { version = "0.12", features = ["stream", "json"] }
reqwest = { version = "0.13", features = ["stream", "json"] }
url = "2.5.0"
toml = "0.9.0"
urlencoding = "2.1.3"
Expand All @@ -62,24 +62,24 @@ regex = "~1.12"
# Error handling
thiserror = "2"
# DB
mongodb = "=3.3"
redis = { version = "0.32", features = ["tokio-comp", "num-bigint"] }
mongodb = "=3.4"
redis = { version = "1.0.2", features = ["tokio-comp", "num-bigint"] }
#macro
utils = { path = "utils" }
# Kubernetes
kube = { version = "~2.0", features = ["runtime", "derive"], optional = true }
k8s-openapi = { version = "0.26", features = ["latest"], optional = true }
kube = { version = "~3.0", features = ["runtime", "derive"], optional = true }
k8s-openapi = { version = "0.27", features = ["latest"], optional = true }
schemars = { version = "1.0", optional = true }
either = { version = "1.13.0", optional = true }
# memory allocator
mimalloc = "~0.1"
# security
base64 = "~0.22.1"
rand = "~0.9"
uuid = { version = "~1.18", features = ["v4"] }
uuid = { version = "~1.19", features = ["v4"] }

[dev-dependencies]
jsonwebkey = { version = "~0.3", features = ["jwt-convert"] }
jsonwebkey = { version = "~0.4", features = ["jwt-convert"] }

[workspace.dependencies]
quote = "1.0"
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Stage 1 build
FROM rust:1.90.0 AS builder
FROM rust:1.92.0 AS builder

WORKDIR /app

Expand Down Expand Up @@ -46,7 +46,7 @@ WORKDIR /app

RUN apt update -y && apt install openssl -y && apt install ca-certificates

COPY --from=builder /app/target/release/openbridge .
COPY --from=builder /app/target/release/bridge .
COPY ./certs ./certs
COPY ./config ./config
COPY ./templates ./templates
Expand All @@ -59,4 +59,4 @@ USER 1001
EXPOSE 8080
EXPOSE 8000

CMD ["./openbridge"]
CMD ["./bridge"]
4 changes: 2 additions & 2 deletions doc/architecture.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[← Back](../#OpenBridge)
[← Back](../#Bridge)

# Architecture

Expand All @@ -7,7 +7,7 @@

<br>

### Technology powering OpenBridge
### Technology powering Bridge

- Front-end:
- HTMX
Expand Down
12 changes: 12 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,11 @@ mini-js:
uglifyjs ./static/js/main.js -o ./static/js/main.js -c -m

build-front:
# get your flavor of tailwindcss
tailwindcss -i ./static/css/input.css -o ./static/css/output.css --minify
# npm i -Dg typescript
tsc
# npm i -g uglify-js
uglifyjs ./static/js/main.js -o ./static/js/main.js -c -m

# --- Local Development Services ---
Expand Down Expand Up @@ -112,6 +115,15 @@ watch-backend:
watch:
bacon --features "notebook lifecycle"

start-port-forward:
sudo iptables -t nat -A OUTPUT -p tcp -d 127.255.255.254 --dport 443 -j DNAT --to-destination 127.255.255.254:8080

stop-port-forward:
sudo iptables -t nat -D OUTPUT -p tcp -d 127.255.255.254 --dport 443 -j DNAT --to-destination 127.255.255.254:8080

check-port-foward:
sudo iptables -t nat -L OUTPUT

# --- Certificates ---
certs:
mkdir certs
Expand Down
4 changes: 2 additions & 2 deletions src/auth/openid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub fn get_openid_provider(provider: OpenIDProvider) -> Result<&'static OpenID>

pub struct OpenID {
client: OIDC,
reqwest_client: reqwest::Client,
reqwest_client: openidconnect::reqwest::Client,
}

pub static OPENID_W3: OnceLock<OpenID> = OnceLock::new();
Expand All @@ -110,7 +110,7 @@ impl OpenID {
.get(table_name)
.ok_or_else(|| BridgeError::TomlLookupError)?;

let reqwest_client = reqwest::Client::new();
let reqwest_client = openidconnect::reqwest::Client::new();

let provider_metadata = core::CoreProviderMetadata::discover_async(
IssuerUrl::new(oidc.url.to_owned())?,
Expand Down
12 changes: 7 additions & 5 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,7 @@ pub fn init_once() -> Configuration {
validation.leeway = 0;

let (config_location_str, database_location_str) = if cfg!(debug_assertions) {
(
"config/configurations_sample.toml",
"config/database_sample.toml",
)
("config/configurations.toml", "config/database_sample.toml")
} else {
("config/configurations.toml", "config/database.toml")
};
Expand Down Expand Up @@ -245,7 +242,12 @@ pub fn init_once() -> Configuration {
)
};

let bridge_url = app_conf["bridge_url"].as_str().unwrap().to_string();
let bridge_url = if cfg!(debug_assertions) {
// make sure you spoof bridge.dev locally
"dev.open.accelerate.science".to_string()
} else {
app_conf["bridge_url"].as_str().unwrap().to_string()
};

Configuration {
encoder,
Expand Down
38 changes: 19 additions & 19 deletions src/logger/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,25 @@ pub fn start_logger(level: LevelFilter, _client: Client, _tx: Sender<()>) {
.with_filter(level),
);

#[cfg(feature = "observe")]
let ts = {
use crate::config::CONFIG;

if let Some((ref api_key, ref endpoint)) = CONFIG.observability_cred {
use crate::db::mongo::DBCONN;

let writer = observability::Observe::new(api_key, endpoint, _client)
.expect("Failed to create observability for logger");
let observe_layer = observability::ObserveEvents::new(
DBCONN.get().expect("DB connection not initialized"),
_tx,
);

ts.with(writer.wrap_layer(level)).with(observe_layer)
} else {
panic!("Observability credentials are not set in the configuration")
}
};
// #[cfg(feature = "observe")]
// let ts = {
// use crate::config::CONFIG;
//
// if let Some((ref api_key, ref endpoint)) = CONFIG.observability_cred {
// use crate::db::mongo::DBCONN;
//
// let writer = observability::Observe::new(api_key, endpoint, _client)
// .expect("Failed to create observability for logger");
// let observe_layer = observability::ObserveEvents::new(
// DBCONN.get().expect("DB connection not initialized"),
// _tx,
// );
//
// ts.with(writer.wrap_layer(level)).with(observe_layer)
// } else {
// panic!("Observability credentials are not set in the configuration")
// }
// };

ts.init()
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::io::Result;

use mimalloc::MiMalloc;

use openbridge::web::start_server;
use bridge::web::start_server;

#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
Expand Down
2 changes: 1 addition & 1 deletion src/web/bridge_middleware/maintenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ where
&& let Some(rg) = MAINTENANCE_WINDOWS.try_read()
&& *rg
{
// OpenBridge under maintenance
// Bridge under maintenance
return Box::pin(async move {
Ok(req.into_response(
HttpResponse::Found()
Expand Down
17 changes: 12 additions & 5 deletions src/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const LIFECYCLE_TIME: Duration = Duration::from_secs(3600);
#[cfg(all(feature = "notebook", feature = "lifecycle"))]
const SIGTERM_FREQ: Duration = Duration::from_secs(5);

/// Starts the OpenBridge server either with or without TLS. If with TLS, please ensure you have the
/// Starts the Bridge server either with or without TLS. If with TLS, please ensure you have the
/// appropriate certs in the `certs` directory.
///
/// # Example
Expand Down Expand Up @@ -118,7 +118,7 @@ pub async fn start_server(with_tls: bool) -> Result<()> {
let server = HttpServer::new(move || {
let tera_data = Data::new(templating::start_template_eng());
let mut context = Context::new();
context.insert("application", "OpenBridge");
context.insert("application", "Bridge");
context.insert("application_version", "v0.1.0");
context.insert("app_name", &CONFIG.app_name);
context.insert("company", &CONFIG.company);
Expand Down Expand Up @@ -180,13 +180,20 @@ pub async fn start_server(with_tls: bool) -> Result<()> {
})
});

let ip_addr = if cfg!(debug_assertions) {
// make sure you map this to spoof bridge.dev on your local machine
"127.255.255.254"
} else {
"0.0.0.0"
};

if with_tls {
// Application level https redirect, but only in release mode
let redirect_handle = if cfg!(not(debug_assertions)) {
Some(tokio::spawn(
HttpServer::new(move || App::new().wrap(HttpRedirect))
.workers(1)
.bind(("0.0.0.0", 8000))?
.bind((ip_addr, 8000))?
.run(),
))
} else {
Expand All @@ -195,7 +202,7 @@ pub async fn start_server(with_tls: bool) -> Result<()> {

server
.bind_rustls_0_23(
("0.0.0.0", 8080),
(ip_addr, 8080),
tls::load_certs("certs/fullchain.cer", "certs/open.accelerate.science.key"),
)?
.run()
Expand All @@ -205,7 +212,7 @@ pub async fn start_server(with_tls: bool) -> Result<()> {
handler.await??;
}
} else {
server.bind(("0.0.0.0", 8080))?.run().await?;
server.bind((ip_addr, 8080))?.run().await?;
}

// shutdown signal
Expand Down
6 changes: 4 additions & 2 deletions src/web/route/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ async fn login(req: HttpRequest) -> Result<HttpResponse> {
async fn callback(
req: HttpRequest,
data: Data<Tera>,
ctx: Data<Context>,
db: Data<&DB>,
cache: Data<Option<&CacheDB>>,
path: web::Path<String>,
Expand All @@ -94,7 +95,7 @@ async fn callback(
let openid = helper::log_with_level!(get_openid_provider(openid_kind), error)?;

// get token from auth server
code_to_response(callback_response.code, req, openid, data, db, cache).await
code_to_response(callback_response.code, req, openid, data, ctx, db, cache).await
}

#[instrument(skip_all, parent = None)]
Expand All @@ -103,6 +104,7 @@ async fn code_to_response(
req: HttpRequest,
openid: &OpenID,
data: Data<Tera>,
ctx: Data<Context>,
db: Data<&DB>,
cache: Data<Option<&CacheDB>>,
) -> Result<HttpResponse> {
Expand Down Expand Up @@ -236,7 +238,7 @@ async fn code_to_response(
.secure(true)
.finish();

let mut ctx = Context::new();
let mut ctx = (**ctx).clone();
ctx.insert("name", &name);
let rendered = helper::log_with_level!(data.render("pages/login_success.html", &ctx), error)?;

Expand Down
15 changes: 14 additions & 1 deletion src/web/route/portal/group_admin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,25 @@ pub(super) async fn group(
Err(_) => (vec![], "".to_string(), "".to_string(), "".to_string()),
};

// Placeholder: need to fetch subscription parameters from services.toml
let subs_expanded: Vec<serde_json::Value> = subs
.iter()
.map(|service_name| {
serde_json::json!({
"type": "openad_model",
"url": "https://dummy.url",
"name": service_name,
"nickname": &service_name.strip_prefix("mcp-").unwrap_or(service_name)[0..4],
})
})
.collect();

let mut ctx = (**context).clone();
ctx.insert("name", &user.user_name);
ctx.insert("user_type", &user.user_type);
ctx.insert("email", &user.email);
ctx.insert("group", &user.groups);
ctx.insert("subscriptions", &subs);
ctx.insert("subscriptions", &subs_expanded);
ctx.insert("group_created_at", &group_created_at);
ctx.insert("group_updated_at", &group_updated_at);
ctx.insert("group_last_updated", &group_last_updated);
Expand Down
16 changes: 15 additions & 1 deletion src/web/route/portal/profile_htmx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,22 @@ impl Profile {

pub fn render(&self, tera: Data<Tera>) -> Result<String> {
let mut context = tera::Context::new();

// Placeholder: need to fetch subscription parameters from services.toml
let subs_expanded: Vec<serde_json::Value> = self.subscriptions
.iter()
.map(|service_name| {
serde_json::json!({
"type": "openad_model",
"url": "https://dummy.url",
"name": service_name,
"nickname": &service_name.strip_prefix("mcp-").unwrap_or(service_name)[0..4],
})
})
.collect();

context.insert("group", &self.groups.join(", "));
context.insert("subscriptions", &self.subscriptions);
context.insert("subscriptions", &subs_expanded);
context.insert("name", &self.name);

Ok(tera.render(PROFILE, &context)?)
Expand Down
2 changes: 1 addition & 1 deletion src/web/route/portal/system_admin/htmx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub struct GroupContent {
pub items: Vec<String>,
}

pub(super) static VIEW_GROUP: &str = "components/group_view.html";
pub(super) static VIEW_GROUP: &str = "components/systems_group.html";
pub(super) static MODIFY_GROUP: &str = "components/group_edit.html";
pub(super) static CREATE_GROUP: &str = "components/group_create.html";

Expand Down
Loading
Loading