From: "Fabian Grünbichler" <f.gruenbichler@proxmox.com>
To: Proxmox Datacenter Manager development discussion
<pdm-devel@lists.proxmox.com>
Subject: Re: [pdm-devel] [PATCH datacenter-manager 2/3] server: api: add support for adding openid realms and openid logins
Date: Fri, 17 Oct 2025 09:57:20 +0200 [thread overview]
Message-ID: <1760684951.paenarjg51.astroid@yuna.none> (raw)
In-Reply-To: <20251014133044.337162-8-s.sterz@proxmox.com>
On October 14, 2025 3:30 pm, Shannon Sterz wrote:
> only supports the new HttpOnly authentication flow, as PDM does not
> support non-HttpOnly authentication at the moment.
>
> Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
> ---
> Cargo.toml | 2 +-
> server/Cargo.toml | 1 +
> server/src/api/access/mod.rs | 2 +
> server/src/api/access/openid.rs | 311 +++++++++++++++++++++++++
> server/src/api/config/access/mod.rs | 2 +
> server/src/api/config/access/openid.rs | 290 +++++++++++++++++++++++
> server/src/auth/mod.rs | 6 +-
> 7 files changed, 612 insertions(+), 2 deletions(-)
> create mode 100644 server/src/api/access/openid.rs
> create mode 100644 server/src/api/config/access/openid.rs
>
> diff --git a/Cargo.toml b/Cargo.toml
> index f820409..39a0b23 100644
> --- a/Cargo.toml
> +++ b/Cargo.toml
> @@ -68,7 +68,7 @@ proxmox-uuid = "1"
>
> # other proxmox crates
> proxmox-acme = "0.5"
> -proxmox-openid = "0.10"
> +proxmox-openid = "1.0.2"
>
> # api implementation creates
> proxmox-config-digest = "1"
> diff --git a/server/Cargo.toml b/server/Cargo.toml
> index 0dfcb6c..94420b4 100644
> --- a/server/Cargo.toml
> +++ b/server/Cargo.toml
> @@ -44,6 +44,7 @@ proxmox-lang.workspace = true
> proxmox-ldap.workspace = true
> proxmox-log.workspace = true
> proxmox-login.workspace = true
> +proxmox-openid.workspace = true
> proxmox-rest-server = { workspace = true, features = [ "templates" ] }
> proxmox-router = { workspace = true, features = [ "cli", "server"] }
> proxmox-rrd.workspace = true
> diff --git a/server/src/api/access/mod.rs b/server/src/api/access/mod.rs
> index 345b22f..a6874a5 100644
> --- a/server/src/api/access/mod.rs
> +++ b/server/src/api/access/mod.rs
> @@ -14,6 +14,7 @@ use proxmox_sortable_macro::sortable;
> use pdm_api_types::{Authid, ACL_PATH_SCHEMA, PRIVILEGES, PRIV_ACCESS_AUDIT};
>
> mod domains;
> +mod openid;
> mod tfa;
> mod users;
>
> @@ -33,6 +34,7 @@ const SUBDIRS: SubdirMap = &sorted!([
> .post(&proxmox_auth_api::api::API_METHOD_CREATE_TICKET_HTTP_ONLY)
> .delete(&proxmox_auth_api::api::API_METHOD_LOGOUT),
> ),
> + ("openid", &openid::ROUTER),
> ("users", &users::ROUTER),
> ]);
>
> diff --git a/server/src/api/access/openid.rs b/server/src/api/access/openid.rs
> new file mode 100644
> index 0000000..5f662f0
> --- /dev/null
> +++ b/server/src/api/access/openid.rs
> @@ -0,0 +1,311 @@
> +//! OpenID redirect/login API
> +use anyhow::{bail, format_err, Error};
> +use hyper::http::request::Parts;
> +use hyper::Response;
> +use pdm_api_types::{OpenIdRealmConfig, OPENID_DEFAULT_SCOPE_LIST, REALM_ID_SCHEMA};
> +use pdm_buildcfg::PDM_RUN_DIR_M;
nit: these two should be listed last..
> +use proxmox_auth_api::api::ApiTicket;
> +use proxmox_login::api::CreateTicketResponse;
and these two below, but - this is the wrong CreateTicketResponse?
should be proxmox_auth_api::types::CreateTicketResponse..
> +use serde_json::{json, Value};
> +
> +use proxmox_auth_api::api::{assemble_csrf_prevention_token, AuthContext};
> +use proxmox_auth_api::ticket::Ticket;
> +use proxmox_router::{
> + http_err, list_subdirs_api_method, ApiHandler, ApiMethod, ApiResponseFuture, Permission,
> + Router, RpcEnvironment, SubdirMap,
> +};
> +use proxmox_schema::{api, BooleanSchema, ObjectSchema, ParameterSchema, StringSchema};
nit: BooleanSchema is not used..
> +use proxmox_sortable_macro::sortable;
> +
> +use proxmox_openid::{OpenIdAuthenticator, OpenIdConfig};
> +
> +use proxmox_access_control::types::{User, EMAIL_SCHEMA, FIRST_NAME_SCHEMA, LAST_NAME_SCHEMA};
> +use proxmox_auth_api::types::Userid;
> +
> +use proxmox_access_control::CachedUserInfo;
and those last five are not sorted
> +
> +use crate::auth;
> +
> +fn openid_authenticator(
> + realm_config: &OpenIdRealmConfig,
> + redirect_url: &str,
> +) -> Result<OpenIdAuthenticator, Error> {
> + let scopes: Vec<String> = realm_config
> + .scopes
> + .as_deref()
> + .unwrap_or(OPENID_DEFAULT_SCOPE_LIST)
> + .split(|c: char| c == ',' || c == ';' || char::is_ascii_whitespace(&c))
> + .filter(|s| !s.is_empty())
> + .map(String::from)
> + .collect();
> +
> + let mut acr_values = None;
> + if let Some(ref list) = realm_config.acr_values {
> + acr_values = Some(
> + list.split(|c: char| c == ',' || c == ';' || char::is_ascii_whitespace(&c))
> + .filter(|s| !s.is_empty())
> + .map(String::from)
> + .collect(),
> + );
> + }
> +
> + let config = OpenIdConfig {
> + issuer_url: realm_config.issuer_url.clone(),
> + client_id: realm_config.client_id.clone(),
> + client_key: realm_config.client_key.clone(),
> + prompt: realm_config.prompt.clone(),
> + scopes: Some(scopes),
> + acr_values,
> + };
> + OpenIdAuthenticator::discover(&config, redirect_url)
> +}
> +
> +#[sortable]
> +pub const API_METHOD_OPENID_LOGIN: ApiMethod = ApiMethod::new_full(
> + &ApiHandler::AsyncHttpBodyParameters(&create_ticket_http_only),
> + ParameterSchema::Object(&ObjectSchema::new(
> + "Get a new ticket as an HttpOnly cookie. Supports tickets via cookies.",
> + &sorted!([
> + ("state", false, &StringSchema::new("OpenId state.").schema()),
> + (
> + "code",
> + false,
> + &StringSchema::new("OpenId authorization code.").schema(),
> + ),
> + (
> + "redirect-url",
> + false,
> + &StringSchema::new(
> + "Redirection Url. The client should set this to used server url.",
> + )
> + .schema(),
> + ),
> + ]),
> + )),
> +)
> +.returns(::proxmox_schema::ReturnType::new(
> + false,
> + &ObjectSchema::new(
and if we use the right CreateTicketResponse type above, we can use the
API_SCHEMA it already has here :)
> + "An authentication ticket with additional infos.",
> + &sorted!([
> + ("username", false, &StringSchema::new("User name.").schema()),
> + (
> + "ticket",
> + true,
> + &StringSchema::new(
> + "Auth ticket, present if http-only was not provided or is false."
> + )
> + .schema()
> + ),
> + ("ticket-info",
> + true,
> + &StringSchema::new(
> + "Informational ticket, can only be used to check if the ticket is expired. Present if http-only was true."
> + ).schema()),
> + (
> + "CSRFPreventionToken",
> + false,
> + &StringSchema::new("Cross Site Request Forgery Prevention Token.").schema(),
> + ),
> + ]),
> + )
> + .schema(),
> +))
> +.protected(true)
> +.access(None, &Permission::World)
> +.reload_timezone(true);
> +
> +fn create_ticket_http_only(
> + _parts: Parts,
> + param: Value,
> + _info: &ApiMethod,
> + rpcenv: Box<dyn RpcEnvironment>,
> +) -> ApiResponseFuture {
> + Box::pin(async move {
> + use proxmox_rest_server::RestEnvironment;
> +
> + let code = param["code"]
> + .as_str()
> + .ok_or_else(|| format_err!("missing non-optional parameter: code"))?
> + .to_owned();
> + let state = param["state"]
> + .as_str()
> + .ok_or_else(|| format_err!("missing non-optional parameter: state"))?
> + .to_owned();
> + let redirect_url = param["redirect-url"]
> + .as_str()
> + .ok_or_else(|| format_err!("missing non-optional parameter: redirect-url"))?
> + .to_owned();
> +
> + let env: &RestEnvironment = rpcenv
> + .as_any()
> + .downcast_ref::<RestEnvironment>()
> + .ok_or_else(|| format_err!("detected wrong RpcEnvironment type"))?;
> +
> + let user_info = CachedUserInfo::new()?;
> + let auth_context = auth::get_auth_context()
> + .ok_or_else(|| format_err!("could not get authentication context"))?;
> +
> + let mut tested_username = None;
> +
> + let result = (|| {
is there a reason to not use try_block! for this, like in the rest of
our code base?
> + let (realm, private_auth_state) =
> + OpenIdAuthenticator::verify_public_auth_state(PDM_RUN_DIR_M!(), &state)?;
> +
> + let (domains, _digest) = pdm_config::domains::config()?;
> + let config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
> + let open_id = openid_authenticator(&config, &redirect_url)?;
> + let info = open_id.verify_authorization_code_simple(&code, &private_auth_state)?;
> + let name_attr = config.username_claim.as_deref().unwrap_or("sub");
> +
> + // Try to be compatible with previous versions
> + let try_attr = match name_attr {
> + "subject" => Some("sub"),
> + "username" => Some("preferred_username"),
> + _ => None,
> + };
> +
> + let unique_name = if let Some(name) = info[name_attr]
> + .as_str()
> + .or_else(|| try_attr.and_then(|att| info[att].as_str()))
> + {
> + name.to_owned()
> + } else {
> + bail!("missing claim '{name_attr}'");
> + };
> +
> + let user_id = Userid::try_from(format!("{unique_name}@{realm}"))?;
> + tested_username = Some(unique_name);
> +
> + if !user_info.is_active_user_id(&user_id) {
> + if config.autocreate.unwrap_or(false) {
> + let _lock = proxmox_access_control::user::lock_config()?;
> + let (mut user_config, _digest) = proxmox_access_control::user::config()?;
> +
> + if let Ok(old_user) = user_config.lookup::<User>("user", user_id.as_str()) {
> + if let Some(false) = old_user.enable {
> + bail!("user '{user_id}' is disabled.");
> + } else {
> + bail!("autocreate user failed - '{user_id}' already exists.");
> + }
> + }
> +
> + let firstname = info["given_name"]
> + .as_str()
> + .map(|n| n.to_string())
> + .filter(|n| FIRST_NAME_SCHEMA.parse_simple_value(n).is_ok());
> +
> + let lastname = info["family_name"]
> + .as_str()
> + .map(|n| n.to_string())
> + .filter(|n| LAST_NAME_SCHEMA.parse_simple_value(n).is_ok());
> +
> + let email = info["email"]
> + .as_str()
> + .map(|n| n.to_string())
> + .filter(|n| EMAIL_SCHEMA.parse_simple_value(n).is_ok());
> +
> + let user = User {
> + userid: user_id.clone(),
> + comment: None,
> + enable: None,
> + expire: None,
> + firstname,
> + lastname,
> + email,
> + };
> +
> + user_config.set_data(user.userid.as_str(), "user", &user)?;
> + proxmox_access_control::user::save_config(&user_config)?;
> + } else {
> + bail!("user account '{user_id}' missing, disabled or expired.");
> + }
> + }
> +
> + let api_ticket = ApiTicket::Full(user_id.clone());
> + let ticket = Ticket::new(auth_context.auth_prefix(), &api_ticket)?;
> + let token = assemble_csrf_prevention_token(auth_context.csrf_secret(), &user_id);
> + env.log_auth(user_id.as_str());
> +
> + Ok((user_id, ticket, token))
> + })();
> +
> + let (user_id, mut ticket, token) = result.map_err(|err| {
> + let msg = err.to_string();
> + env.log_failed_auth(tested_username, &msg);
this is copied over from PBS, but isn't this also kinda wrong? not every
error above is a failed auth.. at least if we compare error handling
here with the one in the regular ticket flow, this is inconsistent..
might rather be follow-up material after closer thought though, and
ensuring PBS and PDM behave the same afterwards..
> + http_err!(UNAUTHORIZED, "{msg}")
> + })?;
> +
> + let cookie = format!(
> + "{}={}; Secure; SameSite=Lax; HttpOnly; Path=/;",
> + auth_context.prefixed_auth_cookie_name(),
> + ticket.sign(auth_context.keyring(), None)?,
> + );
> +
> + let response = Response::builder()
> + .header(hyper::http::header::CONTENT_TYPE, "application/json")
> + .header(hyper::header::SET_COOKIE, cookie);
> +
> + let data = CreateTicketResponse {
> + csrfprevention_token: Some(token),
> + clustername: None,
this is then removed
> + ticket: None,
> + ticket_info: Some(ticket.ticket_info()),
> + username: user_id.to_string(),
as well as the to_string here
> + };
> +
> + Ok(response.body(
> + json!({"data": data, "status": 200, "success": true })
> + .to_string()
> + .into(),
> + )?)
> + })
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + realm: {
> + schema: REALM_ID_SCHEMA,
> + },
> + "redirect-url": {
> + description: "Redirection Url. The client should set this to used server url.",
> + type: String,
String type without a schema :-/
> + },
> + },
> + },
> + returns: {
> + description: "Redirection URL.",
> + type: String,
> + },
> + access: {
> + description: "Anyone can access this (before the user is authenticated).",
> + permission: &Permission::World,
> + },
> +)]
> +/// Create OpenID Redirect Session
> +pub fn openid_auth_url(
> + realm: String,
> + redirect_url: String,
> + _rpcenv: &mut dyn RpcEnvironment,
> +) -> Result<String, Error> {
> + let (domains, _digest) = pdm_config::domains::config()?;
> + let config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
> +
> + let open_id = openid_authenticator(&config, &redirect_url)?;
> +
> + let url = open_id.authorize_url(PDM_RUN_DIR_M!(), &realm)?;
> +
> + Ok(url)
> +}
> +
> +#[sortable]
> +const SUBDIRS: SubdirMap = &sorted!([
> + ("login", &Router::new().post(&API_METHOD_OPENID_LOGIN)),
> + ("auth-url", &Router::new().post(&API_METHOD_OPENID_AUTH_URL)),
> +]);
> +
> +pub const ROUTER: Router = Router::new()
> + .get(&list_subdirs_api_method!(SUBDIRS))
> + .subdirs(SUBDIRS);
> diff --git a/server/src/api/config/access/mod.rs b/server/src/api/config/access/mod.rs
> index 7454f53..5776152 100644
> --- a/server/src/api/config/access/mod.rs
> +++ b/server/src/api/config/access/mod.rs
> @@ -4,12 +4,14 @@ use proxmox_sortable_macro::sortable;
>
> mod ad;
> mod ldap;
> +mod openid;
> pub mod tfa;
>
> #[sortable]
> const SUBDIRS: SubdirMap = &sorted!([
> ("tfa", &tfa::ROUTER),
> ("ldap", &ldap::ROUTER),
> + ("openid", &openid::ROUTER),
> ("ad", &ad::ROUTER),
> ]);
>
> diff --git a/server/src/api/config/access/openid.rs b/server/src/api/config/access/openid.rs
> new file mode 100644
> index 0000000..555a1e1
> --- /dev/null
> +++ b/server/src/api/config/access/openid.rs
> @@ -0,0 +1,290 @@
> +use ::serde::{Deserialize, Serialize};
> +/// Configure OpenId realms
> +use anyhow::Error;
> +use pdm_api_types::ConfigDigest;
same here as for the other module
> +use serde_json::Value;
> +
> +use proxmox_router::{http_bail, Permission, Router, RpcEnvironment};
> +use proxmox_schema::{api, param_bail};
> +
> +use pdm_api_types::{
> + OpenIdRealmConfig, OpenIdRealmConfigUpdater, PRIV_REALM_ALLOCATE, PRIV_SYS_AUDIT,
> + REALM_ID_SCHEMA,
> +};
> +
> +use pdm_config::domains;
> +
> +#[api(
> + input: {
> + properties: {},
> + },
> + returns: {
> + description: "List of configured OpenId realms.",
> + type: Array,
> + items: { type: OpenIdRealmConfig },
> + },
> + access: {
> + permission: &Permission::Privilege(&["access", "domains"], PRIV_REALM_ALLOCATE, false),
> + },
> +)]
> +/// List configured OpenId realms
> +pub fn list_openid_realms(
> + _param: Value,
> + rpcenv: &mut dyn RpcEnvironment,
> +) -> Result<Vec<OpenIdRealmConfig>, Error> {
> + let (config, digest) = domains::config()?;
> +
> + let list = config.convert_to_typed_array("openid")?;
> +
> + rpcenv["digest"] = hex::encode(digest).into();
> +
> + Ok(list)
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + config: {
> + type: OpenIdRealmConfig,
> + flatten: true,
> + },
> + },
> + },
> + access: {
> + permission: &Permission::Privilege(&["access", "domains"], PRIV_REALM_ALLOCATE, false),
> + },
> +)]
> +/// Create a new OpenId realm
> +pub fn create_openid_realm(config: OpenIdRealmConfig) -> Result<(), Error> {
> + let _lock = domains::lock_config()?;
> +
> + let (mut domains, _digest) = domains::config()?;
> +
> + if domains::exists(&domains, &config.realm) {
> + param_bail!("realm", "realm '{}' already exists.", config.realm);
> + }
> +
> + if let Some(true) = config.default {
> + domains::unset_default_realm(&mut domains)?;
> + }
> +
> + domains.set_data(&config.realm, "openid", &config)?;
> +
> + domains::save_config(&domains)?;
> +
> + Ok(())
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + realm: {
> + schema: REALM_ID_SCHEMA,
> + },
> + digest: {
> + optional: true,
> + type: ConfigDigest,
> + },
> + },
> + },
> + access: {
> + permission: &Permission::Privilege(&["access", "domains"], PRIV_REALM_ALLOCATE, false),
> + },
> +)]
> +/// Remove a OpenID realm configuration
> +pub fn delete_openid_realm(
> + realm: String,
> + digest: Option<ConfigDigest>,
> + _rpcenv: &mut dyn RpcEnvironment,
> +) -> Result<(), Error> {
> + let _lock = domains::lock_config()?;
> +
> + let (mut domains, expected_digest) = domains::config()?;
> + expected_digest.detect_modification(digest.as_ref())?;
> +
> + if domains.sections.remove(&realm).is_none() {
> + http_bail!(NOT_FOUND, "realm '{realm}' does not exist.");
> + }
> +
> + domains::save_config(&domains)?;
> +
> + Ok(())
> +}
> +
> +#[api(
> + input: {
> + properties: {
> + realm: {
> + schema: REALM_ID_SCHEMA,
> + },
> + },
> + },
> + returns: { type: OpenIdRealmConfig },
> + access: {
> + permission: &Permission::Privilege(&["access", "domains"], PRIV_SYS_AUDIT, false),
> + },
> +)]
> +/// Read the OpenID realm configuration
> +pub fn read_openid_realm(
> + realm: String,
> + rpcenv: &mut dyn RpcEnvironment,
> +) -> Result<OpenIdRealmConfig, Error> {
> + let (domains, digest) = domains::config()?;
> +
> + let config = domains.lookup("openid", &realm)?;
> + rpcenv["digest"] = hex::encode(digest).into();
> +
> + Ok(config)
> +}
> +
> +#[api()]
> +#[derive(Serialize, Deserialize)]
> +#[serde(rename_all = "kebab-case")]
> +/// Deletable property name
> +pub enum DeletableProperty {
> + /// Delete the client key.
> + ClientKey,
> + /// Delete the comment property.
> + Comment,
> + /// Delete the default property.
> + Default,
> + /// Delete the autocreate property
> + Autocreate,
> + /// Delete the scopes property
> + Scopes,
> + /// Delete the prompt property
> + Prompt,
> + /// Delete the acr_values property
> + AcrValues,
> +}
> +
> +#[api(
> + protected: true,
> + input: {
> + properties: {
> + realm: {
> + schema: REALM_ID_SCHEMA,
> + },
> + update: {
> + type: OpenIdRealmConfigUpdater,
> + flatten: true,
> + },
> + delete: {
> + description: "List of properties to delete.",
> + type: Array,
> + optional: true,
> + items: {
> + type: DeletableProperty,
> + }
> + },
> + digest: {
> + optional: true,
> + type: ConfigDigest,
> + },
> + },
> + },
> + returns: { type: OpenIdRealmConfig },
> + access: {
> + permission: &Permission::Privilege(&["access", "domains"], PRIV_REALM_ALLOCATE, false),
> + },
> +)]
> +/// Update an OpenID realm configuration
> +pub fn update_openid_realm(
> + realm: String,
> + update: OpenIdRealmConfigUpdater,
> + delete: Option<Vec<DeletableProperty>>,
> + digest: Option<ConfigDigest>,
> + _rpcenv: &mut dyn RpcEnvironment,
> +) -> Result<(), Error> {
> + let _lock = domains::lock_config()?;
> +
> + let (mut domains, expected_digest) = domains::config()?;
> + expected_digest.detect_modification(digest.as_ref())?;
> +
> + let mut config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
> +
> + if let Some(delete) = delete {
> + for delete_prop in delete {
> + match delete_prop {
> + DeletableProperty::ClientKey => {
> + config.client_key = None;
> + }
> + DeletableProperty::Comment => {
> + config.comment = None;
> + }
> + DeletableProperty::Default => {
> + config.default = None;
> + }
> + DeletableProperty::Autocreate => {
> + config.autocreate = None;
> + }
> + DeletableProperty::Scopes => {
> + config.scopes = None;
> + }
> + DeletableProperty::Prompt => {
> + config.prompt = None;
> + }
> + DeletableProperty::AcrValues => {
> + config.acr_values = None;
> + }
> + }
> + }
> + }
> +
> + if let Some(comment) = update.comment {
> + let comment = comment.trim().to_string();
> + if comment.is_empty() {
> + config.comment = None;
> + } else {
> + config.comment = Some(comment);
> + }
> + }
> +
> + if let Some(true) = update.default {
> + domains::unset_default_realm(&mut domains)?;
> + config.default = Some(true);
> + } else {
> + config.default = None;
> + }
> +
> + if let Some(issuer_url) = update.issuer_url {
> + config.issuer_url = issuer_url;
> + }
> + if let Some(client_id) = update.client_id {
> + config.client_id = client_id;
> + }
> +
> + if update.client_key.is_some() {
> + config.client_key = update.client_key;
> + }
> + if update.autocreate.is_some() {
> + config.autocreate = update.autocreate;
> + }
> + if update.scopes.is_some() {
> + config.scopes = update.scopes;
> + }
> + if update.prompt.is_some() {
> + config.prompt = update.prompt;
> + }
> + if update.acr_values.is_some() {
> + config.acr_values = update.acr_values;
> + }
> +
> + domains.set_data(&realm, "openid", &config)?;
> +
> + domains::save_config(&domains)?;
> +
> + Ok(())
> +}
> +
> +const ITEM_ROUTER: Router = Router::new()
> + .get(&API_METHOD_READ_OPENID_REALM)
> + .put(&API_METHOD_UPDATE_OPENID_REALM)
> + .delete(&API_METHOD_DELETE_OPENID_REALM);
> +
> +pub const ROUTER: Router = Router::new()
> + .get(&API_METHOD_LIST_OPENID_REALMS)
> + .post(&API_METHOD_CREATE_OPENID_REALM)
> + .match_all("realm", &ITEM_ROUTER);
> diff --git a/server/src/auth/mod.rs b/server/src/auth/mod.rs
> index 532350d..9413a83 100644
> --- a/server/src/auth/mod.rs
> +++ b/server/src/auth/mod.rs
> @@ -81,7 +81,11 @@ fn setup_auth_context(use_private_key: bool) {
> proxmox_auth_api::set_auth_context(AUTH_CONTEXT.get().unwrap());
> }
>
> -struct PdmAuthContext {
> +pub(crate) fn get_auth_context() -> Option<&'static PdmAuthContext> {
> + AUTH_CONTEXT.get()
> +}
> +
> +pub(crate) struct PdmAuthContext {
> keyring: Keyring,
> csrf_secret: &'static HMACKey,
> }
> --
> 2.47.3
>
>
>
> _______________________________________________
> pdm-devel mailing list
> pdm-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
>
>
>
_______________________________________________
pdm-devel mailing list
pdm-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pdm-devel
next prev parent reply other threads:[~2025-10-17 7:57 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-14 13:30 [pdm-devel] [PATCH datacenter-manager/yew-comp 0/8] openid support for PDM Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH yew-comp 1/5] login_panel/realm_selector: use default realm provided by api Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH yew-comp 2/5] login_panel/realm_selector: add support for openid realm logins Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH yew-comp 3/5] auth view: add openid icon to openid menu option Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH yew-comp 4/5] auth edit openid: add a default realm checkbox Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH yew-comp 5/5] utils/login panel: move openid redirection authorization helper to utils Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH datacenter-manager 1/3] api-types: add default field to openid realm config Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH datacenter-manager 2/3] server: api: add support for adding openid realms and openid logins Shannon Sterz
2025-10-17 7:57 ` Fabian Grünbichler [this message]
2025-10-17 13:36 ` Shannon Sterz
2025-10-14 13:30 ` [pdm-devel] [PATCH datacenter-manager 3/3] ui: enable openid realms in realm panel Shannon Sterz
2025-10-17 8:01 ` [pdm-devel] [PATCH datacenter-manager/yew-comp 0/8] openid support for PDM Fabian Grünbichler
2025-10-17 14:36 ` Shannon Sterz
2025-10-17 14:13 ` [pdm-devel] Superseded: " Shannon Sterz
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1760684951.paenarjg51.astroid@yuna.none \
--to=f.gruenbichler@proxmox.com \
--cc=pdm-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox