Skip to content
Closed
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 central/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ shared = { workspace = true }
serde = { workspace = true }
futures = { workspace = true }
serde_json = "1"
rand = "0.9"
reqwest = { version = "0.12", default_features = false, features = ["default-tls"] }
anyhow = { workspace = true }
tracing = { workspace = true }
Expand All @@ -22,3 +21,4 @@ urlencoding = "2.1.3"
icinga-client = { git = "https://github.com/samply/icinga-client.git", tag = "v0.1.0", version = "0.1.0" }
toml = "0.8.20"
time = { version = "0.3.41", features = ["serde", "parsing", "macros", "formatting"] }
rand = "0.9.1"
7 changes: 2 additions & 5 deletions central/src/auth/authentik/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ use tracing::{debug, info};

use crate::CLIENT;

use super::{
provider::{compare_provider, get_provider_id},
AuthentikConfig,
};
use super::{client_type, provider::{compare_provider, get_provider_id}, AuthentikConfig};

pub fn generate_app_values(provider: i64, client_id: &str) -> Value {
json!({
Expand Down Expand Up @@ -105,7 +102,7 @@ pub async fn compare_app_provider(
secret: &str,
conf: &AuthentikConfig,
) -> anyhow::Result<bool> {
let client_id = oidc_client_config.client_type(name);
let client_id = client_type(oidc_client_config, name);
let provider_pk = get_provider_id(&client_id, conf).await;
match provider_pk {
Some(pr_id) => {
Expand Down
49 changes: 19 additions & 30 deletions central/src/auth/authentik/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
mod app;
mod group;
mod provider;
#[cfg(test)]
mod test;

use crate::auth::generate_secret;
use std::sync::Mutex;
use crate::CLIENT;
use anyhow::bail;
use app::{check_app_result, compare_app_provider, get_application};
use beam_lib::reqwest::{self, Url};
use clap::Parser;
use group::create_groups;
use provider::{compare_provider, generate_provider_values, get_provider, get_provider_id};
use reqwest::StatusCode;
use provider::{compare_provider, generate_provider_values, get_provider};
use reqwest::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use shared::{OIDCConfig, SecretResult};
use tracing::{debug, info};
use crate::auth::authentik::provider::check_set_federation_id;
use crate::auth::authentik::provider::{check_set_federation_id, generate_provider, update_provider};
use crate::auth::generate_secret;

#[derive(Debug, Parser, Clone)]
pub struct AuthentikConfig {
Expand Down Expand Up @@ -100,37 +101,24 @@ pub async fn create_app_provider(
oidc_client_config: &OIDCConfig,
conf: &AuthentikConfig,
) -> anyhow::Result<SecretResult> {
combine_app_provider(name, oidc_client_config, conf).await
}

pub async fn combine_app_provider(
name: &str,
oidc_client_config: &OIDCConfig,
conf: &AuthentikConfig,
) -> anyhow::Result<SecretResult> {
let client_id = oidc_client_config.client_type(name);
let client_id = client_type(oidc_client_config, name);
let secret = if !oidc_client_config.is_public {
generate_secret()
} else {
String::with_capacity(0)
};
let generated_provider =
let generated_provider: Value =
generate_provider_values(&client_id, oidc_client_config, &secret, conf).await?;
debug!("Provider Values: {:#?}", generated_provider);
let provider_res = CLIENT
.post(conf.authentik_url.join("api/v3/providers/oauth2/")?)
.bearer_auth(&conf.authentik_service_api_key)
.json(&generated_provider)
.send()
.await?;
let provider_res: Response = generate_provider(&generated_provider, conf).await?;
// Create groups for this client
create_groups(name, conf).await?;
debug!("Result Provider: {:#?}", provider_res);
match provider_res.status() {
StatusCode::CREATED => {
let res_provider: serde_json::Value = provider_res.json().await?;
let provider_id = res_provider.get("pk").and_then(|v| v.as_i64()).unwrap();
let provider_name = res_provider.get("name").and_then(|v| v.as_str()).unwrap();
let provider_id = res_provider["pk"].as_i64().expect("provider_id have to be present");
let provider_name = res_provider["name"].as_str().expect("provider_name have to be present");
// check and set federation_id
check_set_federation_id(&name, provider_id, conf, oidc_client_config).await?;
debug!("{:?}", provider_id);
Expand Down Expand Up @@ -180,14 +168,7 @@ pub async fn combine_app_provider(
);
}
} else {
let res = CLIENT
.patch(conf.authentik_url.join(&format!(
"api/v3/providers/oauth2/{}/",
get_provider_id(&client_id, conf).await.unwrap()
))?)
.bearer_auth(&conf.authentik_service_api_key)
.json(&generated_provider)
.send()
let res = update_provider(&generated_provider, &client_id, conf)
.await?
.status()
.is_success()
Expand Down Expand Up @@ -249,4 +230,12 @@ async fn get_mappings_uuids(
);
}
result
}

pub fn client_type(oidc_client_config: &OIDCConfig, name: &str) -> String {
format!("{}-{}", name, if oidc_client_config.is_public { "public" } else { "private" })
}
//use case federation id
pub fn flipped_client_type(oidc_client_config: &OIDCConfig, name: &str) -> String {
format!("{}-{}", name, if oidc_client_config.is_public { "private" } else { "public" })
}
51 changes: 42 additions & 9 deletions central/src/auth/authentik/provider.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use anyhow::{Context, Ok};
use reqwest::Response;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use shared::OIDCConfig;
use tracing::debug;

use crate::CLIENT;

use super::{AuthentikConfig, FlowPropertymapping};
use super::{flipped_client_type, AuthentikConfig, FlowPropertymapping};

#[derive(Debug, Serialize, Deserialize)]
pub struct RedirectURIS {
Expand Down Expand Up @@ -63,6 +64,38 @@ pub async fn generate_provider_values(
Ok(json)
}

pub async fn generate_provider(
generated_provider: &Value,
conf: &AuthentikConfig,
) -> anyhow::Result<Response> {
Ok(
CLIENT
.post(conf.authentik_url.join("api/v3/providers/oauth2/")?)
.bearer_auth(&conf.authentik_service_api_key)
.json(generated_provider)
.send()
.await?
)
}

pub async fn update_provider(
provider_values: &Value,
client_id: &str,
conf: &AuthentikConfig,
) -> anyhow::Result<Response> {
Ok(
CLIENT
.patch(conf.authentik_url.join(&format!(
"api/v3/providers/oauth2/{}/",
get_provider_id(&client_id, conf).await.expect("provider id have to be present")
))?)
.bearer_auth(&conf.authentik_service_api_key)
.json(provider_values)
.send()
.await?
)
}

pub async fn get_provider_id(
client_id: &str,
conf: &AuthentikConfig
Expand Down Expand Up @@ -150,13 +183,13 @@ pub fn provider_configs_match(a: &Value, b: &Value) -> bool {
&& redirct_url_match()
}

pub async fn patch_provider(
id: i64,
pub async fn patch_provider_federation(
provider_id: i64,
federation_id: i64,
conf: &AuthentikConfig
) -> anyhow::Result<()> {
//"api/v3/providers/oauth2/70/";
let query_url = conf.authentik_url.join(&format!("api/v3/providers/oauth2/{}/", id))?;
let query_url = conf.authentik_url.join(&format!("api/v3/providers/oauth2/{}/", provider_id))?;
let json = json!({
"jwt_federation_providers": [
federation_id,
Expand All @@ -170,7 +203,7 @@ pub async fn patch_provider(
.await?
.json()
.await?;
debug!("Value search key {id}: set {federation_id}");
debug!("Value search key {provider_id}: set {federation_id}");
// contains at the moment one id
match target_value
["jwt_federation_providers"][0].as_i64() {
Expand All @@ -190,23 +223,23 @@ pub async fn check_set_federation_id(
if oidc_client_config.is_public {
// public
if let Some(private_id) = get_provider_id(
&oidc_client_config.flipped_client_type(client_name),
&flipped_client_type(oidc_client_config, client_name),
conf
).await {
debug!("public");
patch_provider(private_id, provider_id, conf).await
patch_provider_federation(private_id, provider_id, conf).await
} else {
debug!("no jet found for '{}' federation_id", client_name);
Ok(())
}
} else {
// private
if let Some(public_id) = get_provider_id(
&oidc_client_config.flipped_client_type(client_name),
&flipped_client_type(oidc_client_config, client_name),
conf
).await {
debug!("private");
patch_provider(provider_id, public_id, conf).await
patch_provider_federation(provider_id, public_id, conf).await
} else {
debug!("No provider found for '{}' federation_id", client_name);
Ok(())
Expand Down
10 changes: 4 additions & 6 deletions central/src/auth/authentik/test.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
use crate::auth::authentik::app::{check_app_result, compare_app_provider, generate_app_values};
use crate::auth::authentik::group::{create_groups, post_group};
use crate::auth::authentik::provider::{generate_provider_values, get_provider, get_provider_id};
use crate::auth::authentik::{
combine_app_provider, get_application, get_uuid, validate_application, AuthentikConfig,
};
use crate::auth::authentik::{client_type, create_app_provider, get_application, get_uuid, validate_application, AuthentikConfig};
use crate::CLIENT;
use beam_lib::reqwest::{self, Error, StatusCode, Url};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -57,13 +55,13 @@ async fn test_create_client() -> anyhow::Result<()> {
],
};
let (SecretResult::Created(pw) | SecretResult::AlreadyExisted(pw)) =
dbg!(combine_app_provider(name, &client_config, &conf).await?)
dbg!(create_app_provider(name, &client_config, &conf).await?)
else {
panic!("Not created or existed")
};

let provider_pk = get_provider(
&client_config.client_type(name),
&client_type(&client_config, name),
&conf
)
.await?
Expand All @@ -81,7 +79,7 @@ async fn test_create_client() -> anyhow::Result<()> {
],
};
let (SecretResult::Created(pw) | SecretResult::AlreadyExisted(pw)) =
dbg!(combine_app_provider(name, &client_config, &conf).await?)
dbg!(create_app_provider(name, &client_config, &conf).await?)
else {
panic!("Not created or existed")
};
Expand Down
10 changes: 3 additions & 7 deletions central/src/auth/keycloak/client.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::{
auth::{generate_secret, keycloak::add_service_account_roles},
auth::{keycloak::add_service_account_roles},
CLIENT,
};
use anyhow::bail;
use beam_lib::reqwest::{self, StatusCode};
use serde_json::{json, Value};
use shared::{OIDCConfig, SecretResult};

use crate::auth::generate_secret;
use super::{create_groups, KeyCloakConfig};

pub async fn get_client(
Expand Down Expand Up @@ -120,11 +120,7 @@ pub async fn post_client(
oidc_client_config: &OIDCConfig,
conf: &KeyCloakConfig,
) -> anyhow::Result<SecretResult> {
let secret = if !oidc_client_config.is_public {
generate_secret()
} else {
String::with_capacity(0)
};
let secret = generate_secret();
let generated_client = generate_client(name, oidc_client_config, &secret);
let res = CLIENT
.post(&format!(
Expand Down
1 change: 1 addition & 0 deletions central/src/auth/keycloak/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod client;

mod test;

use crate::CLIENT;
Expand Down
6 changes: 3 additions & 3 deletions central/src/auth/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
use rand::Rng;
pub mod authentik;
pub mod keycloak;

pub fn generate_secret() -> String {
use rand::Rng;
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789";
const PASSWORD_LEN: usize = 30;
let mut rng = rand::thread_rng();
let mut rng = rand::rng();

(0..PASSWORD_LEN)
.map(|_| {
let idx = rng.gen_range(0..CHARSET.len());
let idx = rng.random_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
Expand Down
11 changes: 1 addition & 10 deletions shared/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,6 @@ pub struct OIDCConfig {
pub is_public: bool,
pub redirect_urls: Vec<String>,
}
impl OIDCConfig {
pub fn client_type(&self, name: &str) -> String {
format!("{}-{}", name, if self.is_public { "public" } else { "private" })
}
//use case federation id
pub fn flipped_client_type(&self, name: &str) -> String {
format!("{}-{}", name, if self.is_public { "private" } else { "public" })
}
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GitlabClientConfig {
Expand All @@ -44,4 +35,4 @@ pub enum RequestType {
pub struct SecretRequest {
pub request_type: RequestType,
pub secret_type: SecretType,
}
}