diff --git a/geneit_app/.env b/geneit_app/.env
new file mode 100644
index 0000000..b34ba09
--- /dev/null
+++ b/geneit_app/.env
@@ -0,0 +1 @@
+REACT_APP_BACKEND=http://localhost:8000
\ No newline at end of file
diff --git a/geneit_app/src/App.test.tsx b/geneit_app/src/App.test.tsx
deleted file mode 100644
index 2a68616..0000000
--- a/geneit_app/src/App.test.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
- render();
- const linkElement = screen.getByText(/learn react/i);
- expect(linkElement).toBeInTheDocument();
-});
diff --git a/geneit_app/src/api/ApiClient.ts b/geneit_app/src/api/ApiClient.ts
new file mode 100644
index 0000000..e5418b9
--- /dev/null
+++ b/geneit_app/src/api/ApiClient.ts
@@ -0,0 +1,52 @@
+interface APIResponse {
+ data: any;
+ status: number;
+}
+
+export class ApiError extends Error {
+ constructor(message: string, public code: number, public data: any) {
+ super(message);
+ }
+}
+
+export class APIClient {
+ /**
+ * Get backend URL
+ */
+ static backendURL(): string {
+ const URL = process.env.REACT_APP_BACKEND ?? "";
+ if (URL.length === 0) throw new Error("Backend URL undefined!");
+ return URL;
+ }
+
+ /**
+ * Perform a request on the backend
+ */
+ static async exec(args: {
+ uri: string;
+ method: "GET" | "POST" | "DELETE";
+ allowFail?: boolean;
+ jsonData?: any;
+ }): Promise {
+ const res = await fetch(this.backendURL() + args.uri, {
+ method: args.method,
+ body: args.jsonData ? JSON.stringify(args.jsonData) : undefined,
+ headers: {
+ "Content-Type": args.jsonData ? "application/json" : "text/plain",
+ },
+ });
+
+ let data;
+ if (res.headers.get("content-type") === "application/json")
+ data = await res.json();
+ else data = await res.blob();
+
+ if (!args.allowFail && !res.ok)
+ throw new ApiError("Request failed!", res.status, data);
+
+ return {
+ data: data,
+ status: res.status,
+ };
+ }
+}
diff --git a/geneit_app/src/api/ServerApi.ts b/geneit_app/src/api/ServerApi.ts
new file mode 100644
index 0000000..b25fdc0
--- /dev/null
+++ b/geneit_app/src/api/ServerApi.ts
@@ -0,0 +1,47 @@
+import { APIClient } from "./ApiClient";
+
+interface LenConstraint {
+ min: number;
+ max: number;
+}
+
+interface Constraints {
+ mail_len: LenConstraint;
+ user_name_len: LenConstraint;
+ password_len: LenConstraint;
+}
+
+interface OIDCProvider {
+ id: string;
+ name: string;
+}
+
+export interface ServerConfig {
+ constraints: Constraints;
+ mail: string;
+ oidc_providers: OIDCProvider[];
+}
+
+let config: ServerConfig | null = null;
+
+export class ServerApi {
+ /**
+ * Get server configuration
+ */
+ static async LoadConfig(): Promise {
+ config = (
+ await APIClient.exec({
+ uri: "/server/config",
+ method: "GET",
+ })
+ ).data;
+ }
+
+ /**
+ * Get cached configuration
+ */
+ static Config(): ServerConfig {
+ if (config === null) throw new Error("Missing configuration!");
+ return config;
+ }
+}
diff --git a/geneit_app/src/index.tsx b/geneit_app/src/index.tsx
index 032464f..34f9358 100644
--- a/geneit_app/src/index.tsx
+++ b/geneit_app/src/index.tsx
@@ -1,17 +1,30 @@
-import React from 'react';
-import ReactDOM from 'react-dom/client';
-import './index.css';
-import App from './App';
-import reportWebVitals from './reportWebVitals';
+import React from "react";
+import ReactDOM from "react-dom/client";
+import "./index.css";
+import App from "./App";
+import reportWebVitals from "./reportWebVitals";
+import { ServerApi } from "./api/ServerApi";
-const root = ReactDOM.createRoot(
- document.getElementById('root') as HTMLElement
-);
-root.render(
-
-
-
-);
+async function init() {
+ try {
+ await ServerApi.LoadConfig();
+
+ const root = ReactDOM.createRoot(
+ document.getElementById("root") as HTMLElement
+ );
+
+ root.render(
+
+
+
+ );
+ } catch (e) {
+ console.error(e);
+ alert("Echec de l'initialisation de l'application !");
+ }
+}
+
+init();
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
diff --git a/geneit_backend/Cargo.lock b/geneit_backend/Cargo.lock
index bd1efac..2f0d5fa 100644
--- a/geneit_backend/Cargo.lock
+++ b/geneit_backend/Cargo.lock
@@ -19,6 +19,21 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "actix-cors"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e"
+dependencies = [
+ "actix-utils",
+ "actix-web",
+ "derive_more",
+ "futures-util",
+ "log",
+ "once_cell",
+ "smallvec",
+]
+
[[package]]
name = "actix-http"
version = "3.3.1"
@@ -779,6 +794,7 @@ dependencies = [
name = "geneit_backend"
version = "0.1.0"
dependencies = [
+ "actix-cors",
"actix-remote-ip",
"actix-web",
"anyhow",
diff --git a/geneit_backend/Cargo.toml b/geneit_backend/Cargo.toml
index 8554c2a..4f81b7f 100644
--- a/geneit_backend/Cargo.toml
+++ b/geneit_backend/Cargo.toml
@@ -12,6 +12,7 @@ clap = { version = "4.3.0", features = ["derive", "env"] }
lazy_static = "1.4.0"
anyhow = "1.0.71"
actix-web = "4.3.1"
+actix-cors = "0.6.4"
futures-util = "0.3.28"
diesel = { version = "2.0.4", features = ["postgres"] }
serde = { version = "1.0.163", features = ["derive"] }
diff --git a/geneit_backend/src/app_config.rs b/geneit_backend/src/app_config.rs
index 49b1f90..fa9a0e9 100644
--- a/geneit_backend/src/app_config.rs
+++ b/geneit_backend/src/app_config.rs
@@ -81,19 +81,11 @@ pub struct AppConfig {
pub smtp_password: Option,
/// Password reset URL
- #[clap(
- long,
- env,
- default_value = "http://localhost:3000/reset_password#TOKEN"
- )]
+ #[clap(long, env, default_value = "APP_ORIGIN/reset_password#TOKEN")]
pub reset_password_url: String,
/// Delete account URL
- #[clap(
- long,
- env,
- default_value = "http://localhost:3000/delete_account#TOKEN"
- )]
+ #[clap(long, env, default_value = "APP_ORIGIN/delete_account#TOKEN")]
pub delete_account_url: String,
/// URL where the OpenID configuration can be found
@@ -121,8 +113,8 @@ pub struct AppConfig {
pub oidc_client_secret: String,
/// OpenID login redirect URL
- #[arg(long, env, default_value = "http://localhost:3000/oidc_cb")]
- pub oidc_redirect_url: String,
+ #[arg(long, env, default_value = "APP_ORIGIN/oidc_cb")]
+ oidc_redirect_url: String,
}
lazy_static::lazy_static! {
@@ -159,12 +151,16 @@ impl AppConfig {
/// Get password reset URL
pub fn get_password_reset_url(&self, token: &str) -> String {
- self.reset_password_url.replace("TOKEN", token)
+ self.reset_password_url
+ .replace("APP_ORIGIN", &self.website_origin)
+ .replace("TOKEN", token)
}
/// Get account delete URL
pub fn get_account_delete_url(&self, token: &str) -> String {
- self.delete_account_url.replace("TOKEN", token)
+ self.delete_account_url
+ .replace("APP_ORIGIN", &self.website_origin)
+ .replace("TOKEN", token)
}
/// Get OpenID providers configuration
@@ -181,6 +177,12 @@ impl AppConfig {
name: self.oidc_provider_name.as_str(),
}]
}
+
+ /// Get OIDC callback URL
+ pub fn oidc_redirect_url(&self) -> String {
+ self.oidc_redirect_url
+ .replace("APP_ORIGIN", &self.website_origin)
+ }
}
#[derive(Debug, Clone, serde::Serialize)]
diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs
index 4587b5f..1d58677 100644
--- a/geneit_backend/src/main.rs
+++ b/geneit_backend/src/main.rs
@@ -1,3 +1,4 @@
+use actix_cors::Cors;
use actix_remote_ip::RemoteIPConfig;
use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
@@ -12,6 +13,14 @@ async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
+ .wrap(
+ Cors::default()
+ .allowed_origin(&AppConfig::get().website_origin)
+ .allowed_methods(vec!["GET", "POST"])
+ .allowed_header("X-Auth-Token")
+ .supports_credentials()
+ .max_age(3600),
+ )
.wrap(Logger::default())
.app_data(web::Data::new(RemoteIPConfig {
proxy: AppConfig::get().proxy_ip.clone(),
diff --git a/geneit_backend/src/services/openid_service.rs b/geneit_backend/src/services/openid_service.rs
index 43607a5..db37c9d 100644
--- a/geneit_backend/src/services/openid_service.rs
+++ b/geneit_backend/src/services/openid_service.rs
@@ -96,7 +96,7 @@ pub async fn start_login(prov_id: &str, ip: IpAddr) -> anyhow::Result {
Ok(prov.conf.gen_authorization_url(
prov.prov.client_id,
&state_key,
- &AppConfig::get().oidc_redirect_url,
+ &AppConfig::get().oidc_redirect_url(),
))
}
@@ -133,7 +133,7 @@ pub async fn finish_login(
prov.prov.client_id,
prov.prov.client_secret,
code,
- &AppConfig::get().oidc_redirect_url,
+ &AppConfig::get().oidc_redirect_url(),
)
.await
.map_err(|e| OpenIDServiceError::QueryTokenEndpoint(e.to_string()))?;