Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: auth path prefix support and org_user authentication #329

Merged
merged 2 commits into from
Jan 3, 2025
Merged
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
2 changes: 1 addition & 1 deletion crates/frontend/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
pub async fn fetch_dimensions(
filters: &PaginationParams,
tenant: String,
org_id: String
org_id: String,
) -> Result<PaginatedResponse<DimensionWithMandatory>, ServerFnError> {
let client = reqwest::Client::new();
let host = use_host_server();
Expand Down
4 changes: 3 additions & 1 deletion crates/frontend/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ pub fn app(app_envs: Envs) -> impl IntoView {
path="/admin/organisations"
view=move || {
view! {
<Organisations/>
<Layout show_side_nav=false>
<Organisations/>
</Layout>
}
}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub async fn update_default_config(
key: String,
tenant: String,
payload: DefaultConfigUpdateReq,
org_id: String
org_id: String,
) -> Result<serde_json::Value, String> {
let host = get_host();
let url = format!("{host}/default-config/{key}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub async fn conclude_experiment(
exp_id: String,
variant_id: String,
tenant: &String,
org_id: &String
org_id: &String,
) -> Result<Experiment, String> {
let client = reqwest::Client::new();
let host = get_host();
Expand Down
4 changes: 2 additions & 2 deletions crates/frontend/src/components/function_form/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub async fn create_function(
runtime_version: String,
description: String,
tenant: String,
org_id: String
org_id: String,
) -> Result<Function, String> {
let payload = FunctionCreateRequest {
function_name,
Expand All @@ -29,7 +29,7 @@ pub async fn create_function(
url,
reqwest::Method::POST,
Some(payload),
construct_request_headers(&[("x-tenant", &tenant),("x-org-id", &org_id)])?,
construct_request_headers(&[("x-tenant", &tenant), ("x-org-id", &org_id)])?,
)
.await?;

Expand Down
7 changes: 6 additions & 1 deletion crates/frontend/src/components/workspace_form.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ where
let handle_submit = handle_submit_clone;
async move {
let result = if is_edit {
update_workspace(workspace_name_rs.get(), org_id.get().to_string(), update_payload).await
update_workspace(
workspace_name_rs.get(),
org_id.get().to_string(),
update_payload,
)
.await
} else {
create_workspace(org_id.get().to_string(), create_payload).await
};
Expand Down
54 changes: 32 additions & 22 deletions crates/frontend/src/pages/context_override.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,27 +152,36 @@ pub fn context_override() -> impl IntoView {
let (modal_visible, set_modal_visible) = create_signal(false);
let (delete_id, set_delete_id) = create_signal::<Option<String>>(None);

let page_resource: Resource<(String, String), PageResource> = create_blocking_resource(
move || (tenant_rws.get().0, org_rws.get().0),
|(current_tenant, org_id)| async move {
let empty_list_filters = PaginationParams::all_entries();
let (config_result, dimensions_result, default_config_result) = join!(
fetch_config(current_tenant.to_string(), None, org_id.clone()),
fetch_dimensions(&empty_list_filters, current_tenant.to_string(), org_id.clone()),
fetch_default_config(&empty_list_filters, current_tenant.to_string(), org_id.clone())
);
PageResource {
config: config_result.unwrap_or_default(),
dimensions: dimensions_result
.unwrap_or_default()
.data
.into_iter()
.filter(|d| d.dimension != "variantIds")
.collect(),
default_config: default_config_result.unwrap_or_default().data,
}
},
);
let page_resource: Resource<(String, String), PageResource> =
create_blocking_resource(
move || (tenant_rws.get().0, org_rws.get().0),
|(current_tenant, org_id)| async move {
let empty_list_filters = PaginationParams::all_entries();
let (config_result, dimensions_result, default_config_result) = join!(
fetch_config(current_tenant.to_string(), None, org_id.clone()),
fetch_dimensions(
&empty_list_filters,
current_tenant.to_string(),
org_id.clone()
),
fetch_default_config(
&empty_list_filters,
current_tenant.to_string(),
org_id.clone()
)
);
PageResource {
config: config_result.unwrap_or_default(),
dimensions: dimensions_result
.unwrap_or_default()
.data
.into_iter()
.filter(|d| d.dimension != "variantIds")
.collect(),
default_config: default_config_result.unwrap_or_default().data,
}
},
);

let handle_context_create = Callback::new(move |_| {
set_form_mode.set(Some(FormMode::Create));
Expand Down Expand Up @@ -241,7 +250,8 @@ pub fn context_override() -> impl IntoView {
let confirm_delete = Callback::new(move |_| {
if let Some(id) = delete_id.get().clone() {
spawn_local(async move {
let result = delete_context(tenant_rws.get().0, id, org_rws.get().0).await;
let result =
delete_context(tenant_rws.get().0, id, org_rws.get().0).await;

match result {
Ok(_) => {
Expand Down
3 changes: 2 additions & 1 deletion crates/frontend/src/pages/custom_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ pub fn types_page() -> impl IntoView {
let tenant = tenant_rws.get().0;
let org = org_rws.get().0;
let _ =
delete_type(tenant, row_data.clone().type_name, org).await;
delete_type(tenant, row_data.clone().type_name, org)
.await;
types_resource.refetch();
}
});
Expand Down
3 changes: 2 additions & 1 deletion crates/frontend/src/pages/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ pub fn function_page() -> impl IntoView {
let combined_resource: Resource<(String, String, String), CombinedResource> =
create_blocking_resource(source, |(function_name, tenant, org_id)| async move {
let function_result =
fetch_function(function_name.to_string(), tenant.to_string(), org_id).await;
fetch_function(function_name.to_string(), tenant.to_string(), org_id)
.await;

CombinedResource {
function: function_result.ok(),
Expand Down
5 changes: 4 additions & 1 deletion crates/frontend/src/pages/function/function_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use leptos_router::use_navigate;
use serde::{Deserialize, Serialize};
use superposition_types::database::models::cac::Function;

use crate::{components::function_form::FunctionEditor, types::{OrganisationId, Tenant}};
use crate::{
components::function_form::FunctionEditor,
types::{OrganisationId, Tenant},
};

#[derive(Serialize, Deserialize, Clone, Debug)]
struct CombinedResource {
Expand Down
98 changes: 63 additions & 35 deletions crates/frontend/src/pages/organisations.rs
Original file line number Diff line number Diff line change
@@ -1,58 +1,86 @@
use leptos::*;
use serde_json::{Map, Value};

use crate::api::fetch_organisations;
use crate::components::skeleton::Skeleton;
use crate::components::{
skeleton::Skeleton,
stat::Stat,
table::{
types::{Column, ColumnSortable},
Table,
},
};
use crate::utils::use_host_server;

#[component]
pub fn organisations() -> impl IntoView {
let host = StoredValue::new(use_host_server());
let (organisation_rs, organisation_ws) = create_signal::<Option<String>>(None);

let host = use_host_server();
let organisation_resource = create_local_resource(
|| (),
|_| async { fetch_organisations().await.unwrap_or_default() },
);

let table_columns = create_memo(move |_| {
let host = host.clone();
let navigate = move |_: &str, row: &Map<String, Value>| {
let organisation_id = row["organisation_id"]
.as_str()
.clone()
.unwrap_or_default()
.to_string();
view! {
<button
formaction=format!("{host}/organisations/switch/{organisation_id}")
class="cursor-pointer text-blue-500"
>
{organisation_id}
</button>
}
.into_view()
};

vec![Column::new(
"organisation_id".to_string(),
None,
navigate,
ColumnSortable::No,
)]
});

view! {
<div class="h-screen w-full flex flex-col items-center justify-center">
<form class="p-8">
<Suspense fallback=move || {
view! { <Skeleton /> }
}>
{move || {
let organisations = organisation_resource.get().unwrap_or_default();
let table_rows = organisations.clone()
.into_iter()
.map(|organisation| {
let mut map = Map::new();
map.insert(String::from("organisation_id"), Value::String(organisation));
map
})
.collect::<Vec<Map<String, Value>>>();

view! {
<form action=format!(
"{}/organisations/switch/{}",
host.get_value(),
organisation_rs.get().unwrap_or_default(),
)>
<div>Select Organisation</div>
<select
class="w-[300px] border border-black"
value=organisation_rs.get().unwrap_or_default()
on:change=move |event| {
let organisation = event_target_value(&event);
organisation_ws
.set(
if organisation.as_str() != "" { Some(organisation) } else { None },
);
}
>
<option value=String::from("")>Select Organisation</option>
<For
each=move || organisation_resource.get().clone().unwrap_or_default()
key=|organisation| organisation.clone()
children=move |organisation| {
view! { <option value=organisation.clone() selected={organisation == organisation_rs.get().unwrap_or_default()}>{organisation}</option> }
}
/>
</select>
<button disabled=organisation_rs.get().is_none()>Submit</button>
</form>
<div class="pb-4">
<Stat
heading="Oraganisations"
icon="ri-building-fill"
number={organisations.len().to_string()}
/>
</div>
<Table
class="card-body card rounded-lg w-full bg-base-100 shadow"
cell_class="min-w-48 font-mono".to_string()
rows=table_rows
key_column="id".to_string()
columns=table_columns.get()
/>
}
}}

</Suspense>
</div>
</form>
}
}
56 changes: 33 additions & 23 deletions crates/service_utils/src/middlewares/tenant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use std::rc::Rc;
use superposition_types::TenantConfig;

pub struct OrgWorkspaceMiddlewareFactory {
enable_org_id_header: bool,
enable_workspace_id_header: bool,
enable_org_id: bool,
enable_workspace_id: bool,
}

impl OrgWorkspaceMiddlewareFactory {
pub fn new(enable_org_id_header: bool, enable_workspace_id_header: bool) -> Self {
pub fn new(enable_org_id: bool, enable_workspace_id: bool) -> Self {
Self {
enable_org_id_header,
enable_workspace_id_header,
enable_org_id,
enable_workspace_id,
}
}
}
Expand All @@ -43,16 +43,16 @@ where
fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(OrgWorkspaceMiddleware {
service: Rc::new(service),
enable_org_id_header: self.enable_org_id_header,
enable_workspace_id_header: self.enable_workspace_id_header,
enable_org_id: self.enable_org_id,
enable_workspace_id: self.enable_workspace_id,
}))
}
}

pub struct OrgWorkspaceMiddleware<S> {
service: Rc<S>,
enable_org_id_header: bool,
enable_workspace_id_header: bool,
enable_org_id: bool,
enable_workspace_id: bool,
}

fn extract_org_workspace_from_header(
Expand Down Expand Up @@ -106,8 +106,8 @@ where

fn call(&self, req: ServiceRequest) -> Self::Future {
let srv = self.service.clone();
let enable_org_id = self.enable_org_id_header;
let enable_workspace_id = self.enable_workspace_id_header;
let enable_org_id = self.enable_org_id;
let enable_workspace_id = self.enable_workspace_id;

Box::pin(async move {
let app_state = match req.app_data::<Data<AppState>>() {
Expand All @@ -124,8 +124,10 @@ where
};

let request_path = req.uri().path().replace(&base, "");
let request_pattern =
req.match_pattern().unwrap_or_else(|| request_path.clone());
let request_pattern = req
.match_pattern()
.map(|a| a.replace(&base, ""))
.unwrap_or_else(|| request_path.clone());
let pkg_regex = Regex::new(".*/pkg/.+")
.map_err(|err| error::ErrorInternalServerError(err.to_string()))?;
let assets_regex = Regex::new(".*/assets/.+")
Expand Down Expand Up @@ -196,21 +198,29 @@ where
)
});

// TODO: validate the tenant
let (validated_tenant, tenant_config) = match (org, enable_org_id, workspace, enable_workspace_id) {
(None, true, None, true) => return Err(error::ErrorBadRequest("The parameters org id and workspace id are required, and must be passed through headers/url params/query params. Consult the documentation to know which to use for this endpoint")),
(None, true, _, _) => return Err(error::ErrorBadRequest("The parameter org id is required, and must be passed through headers/url params/query params. Consult the documentation to know which to use for this endpoint")),
(_, _, None, true) => return Err(error::ErrorBadRequest("The parameter workspace id is required, and must be passed through headers/url params/query params. Consult the documentation to know which to use for this endpoint")),
(Some(org_id), _, Some(workspace_id), _) => {
let workspace_id = match (enable_workspace_id, workspace) {
(true, None) => return Err(error::ErrorBadRequest("The parameter workspace id is required, and must be passed through headers/url params/query params. Consult the documentation to know which to use for this endpoint")),
(true, Some(workspace_id)) => workspace_id,
(false, _) => "public",
};

// TODO: validate the tenant, get correct TenantConfig
let (validated_tenant, tenant_config) = match (enable_org_id, org) {
(true, None) => return Err(error::ErrorBadRequest("The parameter org id is required, and must be passed through headers/url params/query params. Consult the documentation to know which to use for this endpoint")),
(true, Some(org_id)) => {
let tenant = format!("{org_id}_{workspace_id}");
(Tenant(tenant), TenantConfig::default())
}
(_, _, _, _) => (Tenant("public".into()), TenantConfig::default()),
},
(false, _) => (Tenant("public".into()), TenantConfig::default()),
};

let organisation = org
.map(String::from)
.map(OrganisationId)
.unwrap_or_default();

req.extensions_mut().insert(validated_tenant);
req.extensions_mut()
.insert(OrganisationId(org.unwrap_or("juspay").into()));
req.extensions_mut().insert(organisation);
req.extensions_mut().insert(tenant_config);
}

Expand Down
Loading
Loading