all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms
@ 2021-06-25  9:20 Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 01/10] depend on proxmox-openid-rs Dietmar Maurer
                   ` (10 more replies)
  0 siblings, 11 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

This implements OpenID connect realms using the new
"proxmox-openid-rs" crate.

Note: The new src/tools/memcom.rs is from Wolfgang.

Changes since v2:
   fix CachedUserInfo by using a shared memory version counter

Changes since v1:
- really fix commit message of first patch
- change api endpoints (/access/openid/{login|auth-url})
- merged all api implementation patches

Changes since preview version (for Fabian):
- fix commit message
- reserve namen 'pam' and 'pbs'
- fix 'make deb'


Dietmar Maurer (10):
  depend on proxmox-openid-rs
  config: new domains.cfg to configure openid realm
  check_acl_path: add /access/domains and /access/openid
  add API to manage openid realms
  cli: add CLI to manage openid realms.
  implement new helper is_active_user_id()
  cleanup user/token is_active() check
  api: add openid redirect/login API
  ui: implement OpenId login
  fix CachedUserInfo by using a shared memory version counter

 Cargo.toml                               |   2 +
 src/api2/access.rs                       |   4 +-
 src/api2/access/domain.rs                |  18 ++
 src/api2/access/openid.rs                | 190 ++++++++++++++++
 src/api2/config/access/mod.rs            |   8 +-
 src/api2/config/access/openid.rs         | 274 +++++++++++++++++++++++
 src/bin/proxmox-backup-manager.rs        |   1 +
 src/bin/proxmox_backup_manager/mod.rs    |   2 +
 src/bin/proxmox_backup_manager/openid.rs |  99 ++++++++
 src/config.rs                            |   1 +
 src/config/acl.rs                        |   8 +-
 src/config/cached_user_info.rs           |  48 ++--
 src/config/domains.rs                    | 173 ++++++++++++++
 src/config/user.rs                       |  38 ++++
 src/tools.rs                             |   3 +
 src/tools/memcom.rs                      | 159 +++++++++++++
 www/Application.js                       |   8 +-
 www/LoginView.js                         | 100 ++++++++-
 www/Utils.js                             |   8 +
 19 files changed, 1115 insertions(+), 29 deletions(-)
 create mode 100644 src/api2/access/openid.rs
 create mode 100644 src/api2/config/access/openid.rs
 create mode 100644 src/bin/proxmox_backup_manager/openid.rs
 create mode 100644 src/config/domains.rs
 create mode 100644 src/tools/memcom.rs

-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 01/10] depend on proxmox-openid-rs
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 02/10] config: new domains.cfg to configure openid realm Dietmar Maurer
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 Cargo.toml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Cargo.toml b/Cargo.toml
index 976f18bc..adb58c7b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -84,6 +84,8 @@ crossbeam-channel = "0.5"
 
 proxmox-acme-rs = "0.2.1"
 
+proxmox-openid = "0.6.0"
+
 [features]
 default = []
 #valgrind = ["valgrind_request"]
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 02/10] config: new domains.cfg to configure openid realm
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 01/10] depend on proxmox-openid-rs Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 03/10] check_acl_path: add /access/domains and /access/openid Dietmar Maurer
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

Or other realmy types...
---
 src/api2/access/domain.rs |  21 +++++++
 src/config.rs             |   1 +
 src/config/domains.rs     | 120 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 src/config/domains.rs

diff --git a/src/api2/access/domain.rs b/src/api2/access/domain.rs
index a2fd746d..2dff9d32 100644
--- a/src/api2/access/domain.rs
+++ b/src/api2/access/domain.rs
@@ -38,10 +38,31 @@ use crate::api2::types::*;
 )]
 /// Authentication domain/realm index.
 fn list_domains() -> Result<Value, Error> {
+
     let mut list = Vec::new();
+
     list.push(json!({ "realm": "pam", "comment": "Linux PAM standard authentication", "default": true }));
     list.push(json!({ "realm": "pbs", "comment": "Proxmox Backup authentication server" }));
+
+    let (config, _digest) = crate::config::domains::config()?;
+
+    for (realm, (section_type, v)) in config.sections.iter() {
+        let mut item = json!({
+            "type": section_type,
+            "realm": realm,
+        });
+
+        if v["comment"].as_str().is_some() {
+            item["comment"] = v["comment"].clone();
+        }
+        list.push(item);
+
+    }
+
     Ok(list.into())
+
+
+
 }
 
 pub const ROUTER: Router = Router::new()
diff --git a/src/config.rs b/src/config.rs
index b9cd6281..329315ec 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -31,6 +31,7 @@ pub mod drive;
 pub mod media_pool;
 pub mod tape_encryption_keys;
 pub mod tape_job;
+pub mod domains;
 
 /// Check configuration directory permissions
 ///
diff --git a/src/config/domains.rs b/src/config/domains.rs
new file mode 100644
index 00000000..703acfe9
--- /dev/null
+++ b/src/config/domains.rs
@@ -0,0 +1,120 @@
+use anyhow::{Error};
+use lazy_static::lazy_static;
+use std::collections::HashMap;
+use serde::{Serialize, Deserialize};
+
+use proxmox::api::{
+    api,
+    schema::*,
+    section_config::{
+        SectionConfig,
+        SectionConfigData,
+        SectionConfigPlugin,
+    }
+};
+
+use proxmox::tools::fs::{
+    open_file_locked,
+    replace_file,
+    CreateOptions,
+};
+
+use crate::api2::types::*;
+
+lazy_static! {
+    pub static ref CONFIG: SectionConfig = init();
+}
+
+
+#[api(
+    properties: {
+        realm: {
+            schema: REALM_ID_SCHEMA,
+        },
+        "issuer-url": {
+            description: "OpenID Issuer Url",
+            type: String,
+        },
+        "client-id": {
+            description: "OpenID Client ID",
+            type: String,
+        },
+        "client-key": {
+            description: "OpenID Client Key",
+            type: String,
+            optional: true,
+        },
+        comment: {
+            optional: true,
+            schema: SINGLE_LINE_COMMENT_SCHEMA,
+        },
+    },
+)]
+#[derive(Serialize,Deserialize)]
+#[serde(rename_all="kebab-case")]
+/// OpenID configuration properties.
+pub struct OpenIdRealmConfig {
+    pub realm: String,
+    pub issuer_url: String,
+    pub client_id: String,
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub client_key: Option<String>,
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub comment: Option<String>,
+}
+
+fn init() -> SectionConfig {
+    let obj_schema = match OpenIdRealmConfig::API_SCHEMA {
+        Schema::Object(ref obj_schema) => obj_schema,
+        _ => unreachable!(),
+    };
+
+    let plugin = SectionConfigPlugin::new("openid".to_string(), Some(String::from("realm")), obj_schema);
+    let mut config = SectionConfig::new(&REALM_ID_SCHEMA);
+    config.register_plugin(plugin);
+
+    config
+}
+
+pub const DOMAINS_CFG_FILENAME: &str = "/etc/proxmox-backup/domains.cfg";
+pub const DOMAINS_CFG_LOCKFILE: &str = "/etc/proxmox-backup/.domains.lck";
+
+/// Get exclusive lock
+pub fn lock_config() -> Result<std::fs::File, Error> {
+    open_file_locked(DOMAINS_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)
+}
+
+pub fn config() -> Result<(SectionConfigData, [u8;32]), Error> {
+
+    let content = proxmox::tools::fs::file_read_optional_string(DOMAINS_CFG_FILENAME)?
+        .unwrap_or_else(|| "".to_string());
+
+    let digest = openssl::sha::sha256(content.as_bytes());
+    let data = CONFIG.parse(DOMAINS_CFG_FILENAME, &content)?;
+    Ok((data, digest))
+}
+
+pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
+    let raw = CONFIG.write(DOMAINS_CFG_FILENAME, &config)?;
+
+    let backup_user = crate::backup::backup_user()?;
+    let mode = nix::sys::stat::Mode::from_bits_truncate(0o0640);
+    // set the correct owner/group/permissions while saving file
+    // owner(rw) = root, group(r)= backup
+    let options = CreateOptions::new()
+        .perm(mode)
+        .owner(nix::unistd::ROOT)
+        .group(backup_user.gid);
+
+    replace_file(DOMAINS_CFG_FILENAME, raw.as_bytes(), options)?;
+
+    Ok(())
+}
+
+// shell completion helper
+pub fn complete_realm_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+    match config() {
+        Ok((data, _digest)) => data.sections.iter().map(|(id, _)| id.to_string()).collect(),
+        Err(_) => return vec![],
+    }
+}
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 03/10] check_acl_path: add /access/domains and /access/openid
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 01/10] depend on proxmox-openid-rs Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 02/10] config: new domains.cfg to configure openid realm Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 04/10] add API to manage openid realms Dietmar Maurer
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/config/acl.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/config/acl.rs b/src/config/acl.rs
index 04d42854..e468586e 100644
--- a/src/config/acl.rs
+++ b/src/config/acl.rs
@@ -283,11 +283,17 @@ pub fn check_acl_path(path: &str) -> Result<(), Error> {
                 return Ok(());
             }
             match components[1] {
-                "acl" | "users" => {
+                "acl" | "users" | "domains" => {
                     if components_len == 2 {
                         return Ok(());
                     }
                 }
+                // /access/openid/{endpoint}
+                "openid" => {
+                    if components_len <= 3 {
+                        return Ok(());
+                    }
+                }
                 _ => {}
             }
         }
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 04/10] add API to manage openid realms
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (2 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 03/10] check_acl_path: add /access/domains and /access/openid Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI " Dietmar Maurer
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/api2/config/access/mod.rs    |   8 +-
 src/api2/config/access/openid.rs | 264 +++++++++++++++++++++++++++++++
 2 files changed, 271 insertions(+), 1 deletion(-)
 create mode 100644 src/api2/config/access/openid.rs

diff --git a/src/api2/config/access/mod.rs b/src/api2/config/access/mod.rs
index 659815e0..6e7d98be 100644
--- a/src/api2/config/access/mod.rs
+++ b/src/api2/config/access/mod.rs
@@ -1,9 +1,15 @@
 use proxmox::api::{Router, SubdirMap};
 use proxmox::list_subdirs_api_method;
+use proxmox::{identity, sortable};
 
 pub mod tfa;
+pub mod openid;
 
-const SUBDIRS: SubdirMap = &[("tfa", &tfa::ROUTER)];
+#[sortable]
+const SUBDIRS: SubdirMap = &sorted!([
+    ("openid", &openid::ROUTER),
+    ("tfa", &tfa::ROUTER),
+]);
 
 pub const ROUTER: Router = Router::new()
     .get(&list_subdirs_api_method!(SUBDIRS))
diff --git a/src/api2/config/access/openid.rs b/src/api2/config/access/openid.rs
new file mode 100644
index 00000000..15fddaf0
--- /dev/null
+++ b/src/api2/config/access/openid.rs
@@ -0,0 +1,264 @@
+/// Configure OpenId realms
+
+use anyhow::{bail, Error};
+use serde_json::Value;
+use ::serde::{Deserialize, Serialize};
+
+use proxmox::api::{api, Permission, Router, RpcEnvironment};
+
+use crate::config::domains::{self, OpenIdRealmConfig};
+use crate::config::acl::{PRIV_SYS_AUDIT, PRIV_PERMISSIONS_MODIFY};
+use crate::api2::types::*;
+
+#[api(
+    input: {
+        properties: {},
+    },
+    returns: {
+        description: "List of configured OpenId realms.",
+        type: Array,
+        items: { type: OpenIdRealmConfig },
+    },
+    access: {
+        permission: &Permission::Privilege(&["access", "domains"], PRIV_SYS_AUDIT, false),
+    },
+)]
+/// List configured OpenId realms
+pub fn list_openid_realms(
+    _param: Value,
+    mut rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Vec<OpenIdRealmConfig>, Error> {
+
+    let (config, digest) = domains::config()?;
+
+    let list = config.convert_to_typed_array("openid")?;
+
+    rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
+
+    Ok(list)
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            config: {
+                type: OpenIdRealmConfig,
+                flatten: true,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["access", "domains"], PRIV_PERMISSIONS_MODIFY, 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 config.realm == "pbs" ||
+        config.realm == "pam" ||
+        domains.sections.get(&config.realm).is_some()
+    {
+        bail!("realm '{}' already exists.", config.realm);
+    }
+
+    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,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    access: {
+        permission: &Permission::Privilege(&["access", "domains"], PRIV_PERMISSIONS_MODIFY, false),
+    },
+)]
+/// Remove a OpenID realm configuration
+pub fn delete_openid_realm(
+    realm: String,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+
+    let _lock = domains::lock_config()?;
+
+    let (mut domains, expected_digest) = domains::config()?;
+
+    if let Some(ref digest) = digest {
+        let digest = proxmox::tools::hex_to_digest(digest)?;
+        crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
+    }
+
+    if domains.sections.remove(&realm).is_none()  {
+        bail!("realm '{}' does not exist.", realm);
+    }
+
+    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,
+    mut rpcenv: &mut dyn RpcEnvironment,
+) -> Result<OpenIdRealmConfig, Error> {
+
+    let (domains, digest) = domains::config()?;
+
+    let config = domains.lookup("openid", &realm)?;
+
+    rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
+
+    Ok(config)
+}
+
+#[api()]
+#[derive(Serialize, Deserialize)]
+#[serde(rename_all="kebab-case")]
+#[allow(non_camel_case_types)]
+/// Deletable property name
+pub enum DeletableProperty {
+    /// Delete the client key.
+    client_key,
+    /// Delete the comment property.
+    comment,
+}
+
+#[api(
+    protected: true,
+    input: {
+        properties: {
+            realm: {
+                schema: REALM_ID_SCHEMA,
+            },
+            "issuer-url": {
+                description: "OpenID Issuer Url",
+                type: String,
+                optional: true,
+            },
+            "client-id": {
+                description: "OpenID Client ID",
+                type: String,
+                optional: true,
+            },
+            "client-key": {
+                description: "OpenID Client Key",
+                type: String,
+                optional: true,
+            },
+            comment: {
+                schema: SINGLE_LINE_COMMENT_SCHEMA,
+                optional: true,
+            },
+            delete: {
+                description: "List of properties to delete.",
+                type: Array,
+                optional: true,
+                items: {
+                    type: DeletableProperty,
+                }
+            },
+            digest: {
+                optional: true,
+                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+            },
+        },
+    },
+    returns:  { type: OpenIdRealmConfig },
+    access: {
+        permission: &Permission::Privilege(&["access", "domains"], PRIV_PERMISSIONS_MODIFY, false),
+    },
+)]
+/// Update an OpenID realm configuration
+pub fn update_openid_realm(
+    realm: String,
+    issuer_url: Option<String>,
+    client_id: Option<String>,
+    client_key: Option<String>,
+    comment: Option<String>,
+    delete: Option<Vec<DeletableProperty>>,
+    digest: Option<String>,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<(), Error> {
+
+    let _lock = domains::lock_config()?;
+
+    let (mut domains, expected_digest) = domains::config()?;
+
+    if let Some(ref digest) = digest {
+        let digest = proxmox::tools::hex_to_digest(digest)?;
+        crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
+    }
+
+    let mut config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
+
+    if let Some(delete) = delete {
+        for delete_prop in delete {
+            match delete_prop {
+                DeletableProperty::client_key => { config.client_key = None; },
+                DeletableProperty::comment => { config.comment = None; },
+            }
+        }
+    }
+
+    if let Some(comment) = comment {
+        let comment = comment.trim().to_string();
+        if comment.is_empty() {
+            config.comment = None;
+        } else {
+            config.comment = Some(comment);
+        }
+    }
+
+    if let Some(issuer_url) = issuer_url { config.issuer_url = issuer_url; }
+    if let Some(client_id) = client_id { config.client_id = client_id; }
+
+    if client_key.is_some() { config.client_key = client_key; }
+
+    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("id", &ITEM_ROUTER);
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI to manage openid realms.
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (3 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 04/10] add API to manage openid realms Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25 11:41   ` Wolfgang Bumiller
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 06/10] implement new helper is_active_user_id() Dietmar Maurer
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/bin/proxmox-backup-manager.rs        |  1 +
 src/bin/proxmox_backup_manager/mod.rs    |  2 +
 src/bin/proxmox_backup_manager/openid.rs | 99 ++++++++++++++++++++++++
 src/config/domains.rs                    |  9 +++
 4 files changed, 111 insertions(+)
 create mode 100644 src/bin/proxmox_backup_manager/openid.rs

diff --git a/src/bin/proxmox-backup-manager.rs b/src/bin/proxmox-backup-manager.rs
index c3806a31..461d45bd 100644
--- a/src/bin/proxmox-backup-manager.rs
+++ b/src/bin/proxmox-backup-manager.rs
@@ -354,6 +354,7 @@ fn main() {
         .insert("network", network_commands())
         .insert("node", node_commands())
         .insert("user", user_commands())
+        .insert("openid", openid_commands())
         .insert("remote", remote_commands())
         .insert("garbage-collection", garbage_collection_commands())
         .insert("acme", acme_mgmt_cli())
diff --git a/src/bin/proxmox_backup_manager/mod.rs b/src/bin/proxmox_backup_manager/mod.rs
index 21004bbe..a3a16246 100644
--- a/src/bin/proxmox_backup_manager/mod.rs
+++ b/src/bin/proxmox_backup_manager/mod.rs
@@ -24,3 +24,5 @@ mod disk;
 pub use disk::*;
 mod node;
 pub use node::*;
+mod openid;
+pub use openid::*;
diff --git a/src/bin/proxmox_backup_manager/openid.rs b/src/bin/proxmox_backup_manager/openid.rs
new file mode 100644
index 00000000..13915339
--- /dev/null
+++ b/src/bin/proxmox_backup_manager/openid.rs
@@ -0,0 +1,99 @@
+use anyhow::Error;
+use serde_json::Value;
+
+use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
+
+use proxmox_backup::{config, api2, api2::types::REALM_ID_SCHEMA};
+
+
+#[api(
+    input: {
+        properties: {
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+/// List configured OpenId realms
+fn list_openid_realms(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::access::openid::API_METHOD_LIST_OPENID_REALMS;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options()
+        .column(ColumnConfig::new("realm"))
+        .column(ColumnConfig::new("issuer-url"))
+        .column(ColumnConfig::new("comment"));
+
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+#[api(
+    input: {
+        properties: {
+            realm: {
+                schema: REALM_ID_SCHEMA,
+            },
+            "output-format": {
+                schema: OUTPUT_FORMAT,
+                optional: true,
+            },
+        }
+    }
+)]
+
+/// Show OpenID realm configuration
+fn show_openid_realm(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
+
+    let output_format = get_output_format(&param);
+
+    let info = &api2::config::access::openid::API_METHOD_READ_OPENID_REALM;
+    let mut data = match info.handler {
+        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
+        _ => unreachable!(),
+    };
+
+    let options = default_table_format_options();
+    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
+
+    Ok(Value::Null)
+}
+
+pub fn openid_commands() -> CommandLineInterface {
+
+    let cmd_def = CliCommandMap::new()
+        .insert("list", CliCommand::new(&&API_METHOD_LIST_OPENID_REALMS))
+        .insert("show", CliCommand::new(&&API_METHOD_SHOW_OPENID_REALM)
+                .arg_param(&["realm"])
+                .completion_cb("realm", config::domains::complete_openid_realm_name)
+        )
+        .insert("create",
+                CliCommand::new(&api2::config::access::openid::API_METHOD_CREATE_OPENID_REALM)
+                .arg_param(&["realm"])
+                .arg_param(&["realm"])
+                .completion_cb("realm", config::domains::complete_openid_realm_name)
+        )
+        .insert("update",
+                CliCommand::new(&api2::config::access::openid::API_METHOD_UPDATE_OPENID_REALM)
+                .arg_param(&["realm"])
+                .arg_param(&["realm"])
+                .completion_cb("realm", config::domains::complete_openid_realm_name)
+        )
+        .insert("delete",
+                CliCommand::new(&api2::config::access::openid::API_METHOD_DELETE_OPENID_REALM)
+                .arg_param(&["realm"])
+                .arg_param(&["realm"])
+                .completion_cb("realm", config::domains::complete_openid_realm_name)
+        )
+        ;
+
+       cmd_def.into()
+}
diff --git a/src/config/domains.rs b/src/config/domains.rs
index 703acfe9..3cdd4174 100644
--- a/src/config/domains.rs
+++ b/src/config/domains.rs
@@ -118,3 +118,12 @@ pub fn complete_realm_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<
         Err(_) => return vec![],
     }
 }
+
+pub fn complete_openid_realm_name(_arg: &str, _param: &HashMap<String, String>) -> Vec<String> {
+    match config() {
+        Ok((data, _digest)) => data.sections.iter()
+            .filter_map(|(id, (t, _))| if t == "openid" { Some(id.to_string()) } else { None })
+            .collect(),
+        Err(_) => return vec![],
+    }
+}
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 06/10] implement new helper is_active_user_id()
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (4 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI " Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 07/10] cleanup user/token is_active() check Dietmar Maurer
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/config/cached_user_info.rs | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/config/cached_user_info.rs b/src/config/cached_user_info.rs
index c85d643c..a574043f 100644
--- a/src/config/cached_user_info.rs
+++ b/src/config/cached_user_info.rs
@@ -65,10 +65,8 @@ impl CachedUserInfo {
         }
     }
 
-    /// Test if a authentication id is enabled and not expired
-    pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
-        let userid = auth_id.user();
-
+    /// Test if a user_id is enabled and not expired
+    pub fn is_active_user_id(&self, userid: &Userid) -> bool {
         if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
             if !info.enable.unwrap_or(true) {
                 return false;
@@ -78,7 +76,17 @@ impl CachedUserInfo {
                     return false;
                 }
             }
+            true
         } else {
+            false
+        }
+    }
+
+    /// Test if a authentication id is enabled and not expired
+    pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool {
+        let userid = auth_id.user();
+
+        if !self.is_active_user_id(userid) {
             return false;
         }
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 07/10] cleanup user/token is_active() check
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (5 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 06/10] implement new helper is_active_user_id() Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 08/10] api: add openid redirect/login API Dietmar Maurer
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/config/cached_user_info.rs | 25 ++++---------------------
 src/config/user.rs             | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 36 insertions(+), 21 deletions(-)

diff --git a/src/config/cached_user_info.rs b/src/config/cached_user_info.rs
index a574043f..6cb64162 100644
--- a/src/config/cached_user_info.rs
+++ b/src/config/cached_user_info.rs
@@ -7,6 +7,7 @@ use anyhow::{Error, bail};
 use proxmox::api::section_config::SectionConfigData;
 use lazy_static::lazy_static;
 use proxmox::api::UserInformation;
+use proxmox::tools::time::epoch_i64;
 
 use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN};
 use super::user::{ApiToken, User};
@@ -18,8 +19,6 @@ pub struct CachedUserInfo {
     acl_tree: Arc<AclTree>,
 }
 
-fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) } }
-
 struct ConfigCache {
     data: Option<Arc<CachedUserInfo>>,
     last_update: i64,
@@ -35,7 +34,7 @@ impl CachedUserInfo {
 
     /// Returns a cached instance (up to 5 seconds old).
     pub fn new() -> Result<Arc<Self>, Error> {
-        let now = now();
+        let now = epoch_i64();
         { // limit scope
             let cache = CACHED_CONFIG.read().unwrap();
             if (now - cache.last_update) < 5 {
@@ -68,15 +67,7 @@ impl CachedUserInfo {
     /// Test if a user_id is enabled and not expired
     pub fn is_active_user_id(&self, userid: &Userid) -> bool {
         if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) {
-            if !info.enable.unwrap_or(true) {
-                return false;
-            }
-            if let Some(expire) = info.expire {
-                if expire > 0 && expire <= now() {
-                    return false;
-                }
-            }
-            true
+            info.is_active()
         } else {
             false
         }
@@ -92,15 +83,7 @@ impl CachedUserInfo {
 
         if auth_id.is_token() {
             if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", &auth_id.to_string()) {
-                if !info.enable.unwrap_or(true) {
-                    return false;
-                }
-                if let Some(expire) = info.expire {
-                    if expire > 0 && expire <= now() {
-                        return false;
-                    }
-                }
-                return true;
+                return info.is_active();
             } else {
                 return false;
             }
diff --git a/src/config/user.rs b/src/config/user.rs
index ff7e54e4..28e81876 100644
--- a/src/config/user.rs
+++ b/src/config/user.rs
@@ -83,6 +83,22 @@ pub struct ApiToken {
     pub expire: Option<i64>,
 }
 
+impl ApiToken {
+
+    pub fn is_active(&self) -> bool {
+        if !self.enable.unwrap_or(true) {
+            return false;
+        }
+        if let Some(expire) = self.expire {
+            let now =  proxmox::tools::time::epoch_i64();
+            if expire > 0 && expire <= now {
+                return false;
+            }
+        }
+        true
+    }
+}
+
 #[api(
     properties: {
         userid: {
@@ -132,6 +148,22 @@ pub struct User {
     pub email: Option<String>,
 }
 
+impl User {
+
+    pub fn is_active(&self) -> bool {
+        if !self.enable.unwrap_or(true) {
+            return false;
+        }
+        if let Some(expire) = self.expire {
+            let now =  proxmox::tools::time::epoch_i64();
+            if expire > 0 && expire <= now {
+                return false;
+            }
+        }
+        true
+    }
+}
+
 fn init() -> SectionConfig {
     let mut config = SectionConfig::new(&Authid::API_SCHEMA);
 
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 08/10] api: add openid redirect/login API
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (6 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 07/10] cleanup user/token is_active() check Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 09/10] ui: implement OpenId login Dietmar Maurer
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/api2/access.rs               |   4 +-
 src/api2/access/domain.rs        |   3 -
 src/api2/access/openid.rs        | 192 +++++++++++++++++++++++++++++++
 src/api2/config/access/openid.rs |  10 ++
 src/config/domains.rs            |  44 +++++++
 5 files changed, 249 insertions(+), 4 deletions(-)
 create mode 100644 src/api2/access/openid.rs

diff --git a/src/api2/access.rs b/src/api2/access.rs
index 46725c97..e5430f62 100644
--- a/src/api2/access.rs
+++ b/src/api2/access.rs
@@ -26,6 +26,7 @@ pub mod domain;
 pub mod role;
 pub mod tfa;
 pub mod user;
+pub mod openid;
 
 #[allow(clippy::large_enum_variant)]
 enum AuthResult {
@@ -335,7 +336,7 @@ pub fn list_permissions(
     let auth_id = match auth_id {
         Some(auth_id) if auth_id == current_auth_id => current_auth_id,
         Some(auth_id) => {
-            if user_privs & PRIV_SYS_AUDIT != 0 
+            if user_privs & PRIV_SYS_AUDIT != 0
                 || (auth_id.is_token()
                     && !current_auth_id.is_token()
                     && auth_id.user() == current_auth_id.user())
@@ -423,6 +424,7 @@ const SUBDIRS: SubdirMap = &sorted!([
         &Router::new().get(&API_METHOD_LIST_PERMISSIONS)
     ),
     ("ticket", &Router::new().post(&API_METHOD_CREATE_TICKET)),
+    ("openid", &openid::ROUTER),
     ("domains", &domain::ROUTER),
     ("roles", &role::ROUTER),
     ("users", &user::ROUTER),
diff --git a/src/api2/access/domain.rs b/src/api2/access/domain.rs
index 2dff9d32..69809acc 100644
--- a/src/api2/access/domain.rs
+++ b/src/api2/access/domain.rs
@@ -60,9 +60,6 @@ fn list_domains() -> Result<Value, Error> {
     }
 
     Ok(list.into())
-
-
-
 }
 
 pub const ROUTER: Router = Router::new()
diff --git a/src/api2/access/openid.rs b/src/api2/access/openid.rs
new file mode 100644
index 00000000..52ec4311
--- /dev/null
+++ b/src/api2/access/openid.rs
@@ -0,0 +1,192 @@
+//! OpenID redirect/login API
+use std::convert::TryFrom;
+
+use anyhow::{bail, Error};
+
+use serde_json::{json, Value};
+
+use proxmox::api::router::{Router, SubdirMap};
+use proxmox::api::{api, Permission, RpcEnvironment};
+use proxmox::{list_subdirs_api_method};
+use proxmox::{identity, sortable};
+use proxmox::tools::fs::open_file_locked;
+
+use proxmox_openid::OpenIdAuthenticator;
+
+use crate::server::ticket::ApiTicket;
+use crate::tools::ticket::Ticket;
+
+use crate::config::domains::{OpenIdUserAttribute, OpenIdRealmConfig};
+use crate::config::cached_user_info::CachedUserInfo;
+
+use crate::api2::types::*;
+use crate::auth_helpers::*;
+
+#[api(
+    input: {
+        properties: {
+            state: {
+                description: "OpenId state.",
+                type: String,
+            },
+            code: {
+                description: "OpenId authorization code.",
+                type: String,
+            },
+            "redirect-url": {
+                description: "Redirection Url. The client should set this to used server url.",
+                type: String,
+            },
+        },
+    },
+    returns: {
+        properties: {
+            username: {
+                type: String,
+                description: "User name.",
+            },
+            ticket: {
+                type: String,
+                description: "Auth ticket.",
+            },
+            CSRFPreventionToken: {
+                type: String,
+                description: "Cross Site Request Forgery Prevention Token.",
+            },
+        },
+    },
+    protected: true,
+    access: {
+        permission: &Permission::World,
+    },
+)]
+/// Verify OpenID authorization code and create a ticket
+///
+/// Returns: An authentication ticket with additional infos.
+pub fn openid_login(
+    state: String,
+    code: String,
+    redirect_url: String,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Value, Error> {
+    let user_info = CachedUserInfo::new()?;
+
+    let (realm, private_auth_state) =
+        OpenIdAuthenticator::verify_public_auth_state(PROXMOX_BACKUP_RUN_DIR_M!(), &state)?;
+
+    let (domains, _digest) = crate::config::domains::config()?;
+    let config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
+
+    let open_id = config.authenticator(&redirect_url)?;
+
+    let info = open_id.verify_authorization_code(&code, &private_auth_state)?;
+
+    // eprintln!("VERIFIED {} {:?} {:?}", info.subject().as_str(), info.name(), info.email());
+
+    let unique_name = match config.user_attr {
+        None | Some(OpenIdUserAttribute::Subject) => info.subject().as_str(),
+        Some(OpenIdUserAttribute::Username) => {
+            match info.preferred_username() {
+                Some(name) => name.as_str(),
+                None => bail!("missing claim 'preferred_name'"),
+            }
+        }
+        Some(OpenIdUserAttribute::Email) => {
+            match info.email() {
+                Some(name) => name.as_str(),
+                None => bail!("missing claim 'email'"),
+            }
+        }
+    };
+
+    let user_id = Userid::try_from(format!("{}@{}", unique_name, realm))?;
+
+    if !user_info.is_active_user_id(&user_id) {
+        if config.autocreate.unwrap_or(false) {
+            use crate::config::user;
+            let _lock = open_file_locked(user::USER_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?;
+            let user = user::User {
+                userid: user_id.clone(),
+                comment: None,
+                enable: None,
+                expire: None,
+                firstname: info.given_name().and_then(|n| n.get(None)).map(|n| n.to_string()),
+                lastname: info.family_name().and_then(|n| n.get(None)).map(|n| n.to_string()),
+                email: info.email().map(|e| e.to_string()),
+            };
+            let (mut config, _digest) = user::config()?;
+            if config.sections.get(user.userid.as_str()).is_some() {
+                bail!("autocreate user failed - '{}' already exists.", user.userid);
+            }
+            config.set_data(user.userid.as_str(), "user", &user)?;
+            user::save_config(&config)?;
+            // fixme: replace sleep with shared memory change notification
+            std::thread::sleep(std::time::Duration::new(6, 0));
+        } else {
+            bail!("user account '{}' missing, disabled or expired.", user_id);
+        }
+    }
+
+    let api_ticket = ApiTicket::full(user_id.clone());
+    let ticket = Ticket::new("PBS", &api_ticket)?.sign(private_auth_key(), None)?;
+    let token = assemble_csrf_prevention_token(csrf_secret(), &user_id);
+
+    crate::server::rest::auth_logger()?
+        .log(format!("successful auth for user '{}'", user_id));
+
+    Ok(json!({
+        "username": user_id,
+        "ticket": ticket,
+        "CSRFPreventionToken": token,
+    }))
+}
+
+#[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,
+            },
+        },
+    },
+    returns: {
+        description: "Redirection URL.",
+        type: String,
+    },
+    access: {
+        description: "Anyone can access this (before the user is authenticated).",
+        permission: &Permission::World,
+    },
+)]
+/// Create OpenID Redirect Session
+fn openid_auth_url(
+    realm: String,
+    redirect_url: String,
+    _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<String, Error> {
+
+    let (domains, _digest) = crate::config::domains::config()?;
+    let config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
+
+    let open_id = config.authenticator(&redirect_url)?;
+
+    let url = open_id.authorize_url(PROXMOX_BACKUP_RUN_DIR_M!(), &realm)?
+        .to_string();
+
+    Ok(url.into())
+}
+
+#[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/src/api2/config/access/openid.rs b/src/api2/config/access/openid.rs
index 15fddaf0..9325de94 100644
--- a/src/api2/config/access/openid.rs
+++ b/src/api2/config/access/openid.rs
@@ -153,6 +153,8 @@ pub enum DeletableProperty {
     client_key,
     /// Delete the comment property.
     comment,
+    /// Delete the autocreate property
+    autocreate,
 }
 
 #[api(
@@ -177,6 +179,11 @@ pub enum DeletableProperty {
                 type: String,
                 optional: true,
             },
+            autocreate: {
+                description: "Automatically create users if they do not exist.",
+                optional: true,
+                type: bool,
+            },
             comment: {
                 schema: SINGLE_LINE_COMMENT_SCHEMA,
                 optional: true,
@@ -206,6 +213,7 @@ pub fn update_openid_realm(
     issuer_url: Option<String>,
     client_id: Option<String>,
     client_key: Option<String>,
+    autocreate: Option<bool>,
     comment: Option<String>,
     delete: Option<Vec<DeletableProperty>>,
     digest: Option<String>,
@@ -228,6 +236,7 @@ pub fn update_openid_realm(
             match delete_prop {
                 DeletableProperty::client_key => { config.client_key = None; },
                 DeletableProperty::comment => { config.comment = None; },
+                DeletableProperty::autocreate => { config.autocreate = None; },
             }
         }
     }
@@ -245,6 +254,7 @@ pub fn update_openid_realm(
     if let Some(client_id) = client_id { config.client_id = client_id; }
 
     if client_key.is_some() { config.client_key = client_key; }
+    if autocreate.is_some() { config.autocreate = autocreate; }
 
     domains.set_data(&realm, "openid", &config)?;
 
diff --git a/src/config/domains.rs b/src/config/domains.rs
index 3cdd4174..c456bc45 100644
--- a/src/config/domains.rs
+++ b/src/config/domains.rs
@@ -3,6 +3,8 @@ use lazy_static::lazy_static;
 use std::collections::HashMap;
 use serde::{Serialize, Deserialize};
 
+use proxmox_openid::{OpenIdAuthenticator,  OpenIdConfig};
+
 use proxmox::api::{
     api,
     schema::*,
@@ -25,6 +27,22 @@ lazy_static! {
     pub static ref CONFIG: SectionConfig = init();
 }
 
+#[api()]
+#[derive(Eq, PartialEq, Debug, Serialize, Deserialize)]
+#[serde(rename_all = "lowercase")]
+/// Use the value of this attribute/claim as unique user name. It is
+/// up to the identity provider to guarantee the uniqueness. The
+/// OpenID specification only guarantees that Subject ('sub') is unique. Also
+/// make sure that the user is not allowed to change that attribute by
+/// himself!
+pub enum OpenIdUserAttribute {
+    /// Subject (OpenId 'sub' claim)
+    Subject,
+    /// Username (OpenId 'preferred_username' claim)
+    Username,
+    /// Email (OpenId 'email' claim)
+    Email,
+}
 
 #[api(
     properties: {
@@ -48,6 +66,16 @@ lazy_static! {
             optional: true,
             schema: SINGLE_LINE_COMMENT_SCHEMA,
         },
+        autocreate: {
+            description: "Automatically create users if they do not exist.",
+            optional: true,
+            type: bool,
+            default: false,
+        },
+        "user-attr": {
+            type: OpenIdUserAttribute,
+            optional: true,
+        },
     },
 )]
 #[derive(Serialize,Deserialize)]
@@ -61,6 +89,22 @@ pub struct OpenIdRealmConfig {
     pub client_key: Option<String>,
     #[serde(skip_serializing_if="Option::is_none")]
     pub comment: Option<String>,
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub autocreate: Option<bool>,
+    #[serde(skip_serializing_if="Option::is_none")]
+    pub user_attr: Option<OpenIdUserAttribute>,
+}
+
+impl OpenIdRealmConfig {
+
+    pub fn authenticator(&self, redirect_url: &str) -> Result<OpenIdAuthenticator, Error> {
+        let config = OpenIdConfig {
+            issuer_url: self.issuer_url.clone(),
+            client_id: self.client_id.clone(),
+            client_key: self.client_key.clone(),
+        };
+        OpenIdAuthenticator::discover(&config, redirect_url)
+    }
 }
 
 fn init() -> SectionConfig {
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 09/10] ui: implement OpenId login
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (7 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 08/10] api: add openid redirect/login API Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 10/10] fix CachedUserInfo by using a shared memory version counter Dietmar Maurer
  2021-06-29  9:50 ` [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Fabian Grünbichler
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 www/Application.js |   8 +++-
 www/LoginView.js   | 100 ++++++++++++++++++++++++++++++++++++++++++++-
 www/Utils.js       |   8 ++++
 3 files changed, 114 insertions(+), 2 deletions(-)

diff --git a/www/Application.js b/www/Application.js
index c4df600e..20ae26bd 100644
--- a/www/Application.js
+++ b/www/Application.js
@@ -49,9 +49,15 @@ Ext.define('PBS.Application', {
 	var provider = new Ext.state.LocalStorageProvider({ prefix: 'ext-pbs-' });
 	Ext.state.Manager.setProvider(provider);
 
+	let openid_login = false;
+	let param = PBS.Utils.openid_login_param();
+	if (param !== undefined) {
+	    openid_login = true;
+	}
+
 	// show login window if not loggedin
 	var loggedin = Proxmox.Utils.authOK();
-	if (!loggedin) {
+	if (openid_login || !loggedin) {
 	    me.changeView('loginview', true);
 	} else {
 	    me.changeView('mainview', true);
diff --git a/www/LoginView.js b/www/LoginView.js
index 6dd09646..ff3b5540 100644
--- a/www/LoginView.js
+++ b/www/LoginView.js
@@ -2,6 +2,21 @@ Ext.define('PBS.LoginView', {
     extend: 'Ext.container.Container',
     xtype: 'loginview',
 
+    viewModel: {
+	data: {
+	    openid: false,
+	},
+	formulas: {
+	    button_text: function(get) {
+		if (get("openid") === true) {
+		    return gettext("Login (OpenID redirect)");
+		} else {
+		    return gettext("Login");
+		}
+	    },
+	},
+    },
+
     controller: {
 	xclass: 'Ext.app.ViewController',
 
@@ -15,8 +30,33 @@ Ext.define('PBS.LoginView', {
 		return;
 	    }
 
+	    let redirect_url = location.origin;
+
 	    let params = loginForm.getValues();
 
+	    if (this.getViewModel().data.openid === true) {
+		let realm = params.realm;
+		try {
+		    let resp = await PBS.Async.api2({
+			url: '/api2/extjs/access/openid/auth-url',
+			params: {
+			    realm: realm,
+			    "redirect-url": redirect_url,
+			},
+			method: 'POST',
+		    });
+		    window.location = resp.result.data;
+		} catch (error) {
+		    Proxmox.Utils.authClear();
+		    loginForm.unmask();
+		    Ext.MessageBox.alert(
+			gettext('Error'),
+			gettext('OpenId redirect failed. Please try again<br>Error: ' + error),
+		    );
+		}
+		return;
+	    }
+
 	    params.username = params.username + '@' + params.realm;
 	    delete params.realm;
 
@@ -98,6 +138,14 @@ Ext.define('PBS.LoginView', {
 		    window.location.reload();
 		},
 	    },
+	    'field[name=realm]': {
+		change: function(f, value) {
+		    let record = f.store.getById(value);
+		    if (record === undefined) return;
+		    let data = record.data;
+		    this.getViewModel().set("openid", data.type === "openid");
+		},
+	    },
 	    'button[reference=loginButton]': {
 		click: 'submitForm',
 	    },
@@ -116,6 +164,43 @@ Ext.define('PBS.LoginView', {
 			var pwField = this.lookupReference('passwordField');
 			pwField.focus();
 		    }
+
+		    let param = PBS.Utils.openid_login_param();
+		    if (param !== undefined) {
+			Proxmox.Utils.authClear();
+
+			let loginForm = this.lookupReference('loginForm');
+			loginForm.mask(gettext('OpenID login - please wait...'), 'x-mask-loading');
+
+			let redirect_url = location.origin;
+
+			Proxmox.Utils.API2Request({
+			    url: '/api2/extjs/access/openid/login',
+			    params: {
+				state: param.state,
+				code: param.code,
+				"redirect-url": redirect_url,
+			    },
+			    method: 'POST',
+			    failure: function(response) {
+				loginForm.unmask();
+				Ext.MessageBox.alert(
+				    gettext('Error'),
+				    gettext('Login failed. Please try again<br>Error: ' + response.htmlStatus),
+				    function() {
+					window.location = redirect_url;
+				    },
+				);
+			    },
+			    success: function(response, options) {
+				loginForm.unmask();
+				let data = response.result.data;
+				PBS.Utils.updateLoginData(data);
+				PBS.app.changeView('mainview');
+				history.replaceState(null, '', redirect_url + '#pbsDashboard');
+			    },
+			});
+		    }
 		},
 	    },
 	},
@@ -191,6 +276,10 @@ Ext.define('PBS.LoginView', {
 			    itemId: 'usernameField',
 			    reference: 'usernameField',
 			    stateId: 'login-username',
+			    bind: {
+				visible: "{!openid}",
+				disabled: "{openid}",
+			    },
 			},
 			{
 			    xtype: 'textfield',
@@ -199,6 +288,10 @@ Ext.define('PBS.LoginView', {
 			    name: 'password',
 			    itemId: 'passwordField',
 			    reference: 'passwordField',
+			    bind: {
+				visible: "{!openid}",
+				disabled: "{openid}",
+			    },
 			},
 			{
 			    xtype: 'pmxRealmComboBox',
@@ -223,9 +316,14 @@ Ext.define('PBS.LoginView', {
 			    labelWidth: 250,
 			    labelAlign: 'right',
 			    submitValue: false,
+			    bind: {
+				visible: "{!openid}",
+			    },
 			},
 			{
-			    text: gettext('Login'),
+			    bind: {
+				text: "{button_text}",
+			    },
 			    reference: 'loginButton',
 			    formBind: true,
 			},
diff --git a/www/Utils.js b/www/Utils.js
index 6b378355..677f2204 100644
--- a/www/Utils.js
+++ b/www/Utils.js
@@ -326,6 +326,14 @@ Ext.define('PBS.Utils', {
         };
     },
 
+    openid_login_param: function() {
+	let param = Ext.Object.fromQueryString(window.location.search);
+	if (param.state !== undefined && param.code !== undefined) {
+	    return param;
+	}
+	return undefined;
+    },
+
     calculate_dedup_factor: function(gcstatus) {
 	let dedup = 1.0;
 	if (gcstatus['disk-bytes'] > 0) {
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* [pbs-devel] [PATCH proxmox-backup v3 10/10] fix CachedUserInfo by using a shared memory version counter
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (8 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 09/10] ui: implement OpenId login Dietmar Maurer
@ 2021-06-25  9:20 ` Dietmar Maurer
  2021-06-29  9:50 ` [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Fabian Grünbichler
  10 siblings, 0 replies; 13+ messages in thread
From: Dietmar Maurer @ 2021-06-25  9:20 UTC (permalink / raw)
  To: pbs-devel

---
 src/api2/access/openid.rs      |   2 -
 src/config/cached_user_info.rs |  13 ++-
 src/config/user.rs             |   6 ++
 src/tools.rs                   |   3 +
 src/tools/memcom.rs            | 159 +++++++++++++++++++++++++++++++++
 5 files changed, 179 insertions(+), 4 deletions(-)
 create mode 100644 src/tools/memcom.rs

diff --git a/src/api2/access/openid.rs b/src/api2/access/openid.rs
index 52ec4311..27e3c08c 100644
--- a/src/api2/access/openid.rs
+++ b/src/api2/access/openid.rs
@@ -120,8 +120,6 @@ pub fn openid_login(
             }
             config.set_data(user.userid.as_str(), "user", &user)?;
             user::save_config(&config)?;
-            // fixme: replace sleep with shared memory change notification
-            std::thread::sleep(std::time::Duration::new(6, 0));
         } else {
             bail!("user account '{}' missing, disabled or expired.", user_id);
         }
diff --git a/src/config/cached_user_info.rs b/src/config/cached_user_info.rs
index 6cb64162..a4c4604f 100644
--- a/src/config/cached_user_info.rs
+++ b/src/config/cached_user_info.rs
@@ -12,6 +12,7 @@ use proxmox::tools::time::epoch_i64;
 use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN};
 use super::user::{ApiToken, User};
 use crate::api2::types::{Authid, Userid};
+use crate::tools::Memcom;
 
 /// Cache User/Group/Token/Acl configuration data for fast permission tests
 pub struct CachedUserInfo {
@@ -22,11 +23,12 @@ pub struct CachedUserInfo {
 struct ConfigCache {
     data: Option<Arc<CachedUserInfo>>,
     last_update: i64,
+    last_user_cache_generation: usize,
 }
 
 lazy_static! {
     static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new(
-        ConfigCache { data: None, last_update: 0 }
+        ConfigCache { data: None, last_update: 0, last_user_cache_generation: 0 }
     );
 }
 
@@ -35,9 +37,15 @@ impl CachedUserInfo {
     /// Returns a cached instance (up to 5 seconds old).
     pub fn new() -> Result<Arc<Self>, Error> {
         let now = epoch_i64();
+
+        let memcom = Memcom::new()?;
+        let user_cache_generation = memcom.user_cache_generation();
+
         { // limit scope
             let cache = CACHED_CONFIG.read().unwrap();
-            if (now - cache.last_update) < 5 {
+            if (user_cache_generation == cache.last_user_cache_generation) &&
+                ((now - cache.last_update) < 5)
+            {
                 if let Some(ref config) = cache.data {
                     return Ok(config.clone());
                 }
@@ -51,6 +59,7 @@ impl CachedUserInfo {
 
         let mut cache = CACHED_CONFIG.write().unwrap();
         cache.last_update = now;
+        cache.last_user_cache_generation = user_cache_generation;
         cache.data = Some(config.clone());
 
         Ok(config)
diff --git a/src/config/user.rs b/src/config/user.rs
index 28e81876..bdec5fc1 100644
--- a/src/config/user.rs
+++ b/src/config/user.rs
@@ -18,6 +18,7 @@ use proxmox::api::{
 use proxmox::tools::{fs::replace_file, fs::CreateOptions};
 
 use crate::api2::types::*;
+use crate::tools::Memcom;
 
 lazy_static! {
     pub static ref CONFIG: SectionConfig = init();
@@ -270,6 +271,11 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> {
 
     replace_file(USER_CFG_FILENAME, raw.as_bytes(), options)?;
 
+    // increase user cache generation
+    // We use this in CachedUserInfo
+    let memcom = Memcom::new()?;
+    memcom.increase_user_cache_generation();
+
     Ok(())
 }
 
diff --git a/src/tools.rs b/src/tools.rs
index 59599339..3b0bf087 100644
--- a/src/tools.rs
+++ b/src/tools.rs
@@ -38,6 +38,9 @@ pub mod format;
 pub mod fs;
 pub mod fuse_loop;
 
+mod memcom;
+pub use memcom::Memcom;
+
 pub mod json;
 pub mod logrotate;
 pub mod loopdev;
diff --git a/src/tools/memcom.rs b/src/tools/memcom.rs
new file mode 100644
index 00000000..139a789a
--- /dev/null
+++ b/src/tools/memcom.rs
@@ -0,0 +1,159 @@
+//! Memory based communication channel between proxy & daemon for things such as cache
+//! invalidation.
+
+use std::ffi::CString;
+use std::io;
+use std::os::unix::io::AsRawFd;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
+
+use anyhow::{bail, format_err, Error};
+use nix::errno::Errno;
+use nix::fcntl::OFlag;
+use nix::sys::mman::{MapFlags, ProtFlags};
+use nix::sys::stat::Mode;
+use once_cell::sync::OnceCell;
+
+use proxmox::sys::error::SysError;
+use proxmox::tools::fd::Fd;
+use proxmox::tools::mmap::Mmap;
+
+/// In-memory communication channel.
+pub struct Memcom {
+    mmap: Mmap<u8>,
+}
+
+#[repr(C)]
+struct Head {
+    // User (user.cfg) cache generation/version.
+    user_cache_generation: AtomicUsize,
+}
+
+static INSTANCE: OnceCell<Arc<Memcom>>  = OnceCell::new();
+
+const MEMCOM_FILE_PATH: &str = rundir!("/proxmox-backup-memcom");
+
+impl Memcom {
+    
+    /// Open the memory based communication channel singleton.
+    pub fn new() ->  Result<Arc<Self>, Error>  {
+        INSTANCE.get_or_try_init(Self::open).map(Arc::clone)
+    }
+
+    // Actual work of `new`:
+    fn open() ->  Result<Arc<Self>, Error>  {
+        let fd = match open_existing() {
+            Ok(fd) =>  fd,
+            Err(err) if err.not_found() =>  create_new()?,
+            Err(err) =>  bail!("failed to open {} - {}", MEMCOM_FILE_PATH, err),
+        };
+
+        let mmap = unsafe {
+            Mmap::<u8>::map_fd(
+                fd.as_raw_fd(),
+                0,
+                4096,
+                ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+                MapFlags::MAP_SHARED | MapFlags::MAP_NORESERVE | MapFlags::MAP_POPULATE,
+            )?
+        };
+        
+        Ok(Arc::new(Self { mmap }))
+    }
+
+    // Shortcut to get the mapped `Head` as a `Head`.
+    fn head(&self) ->  &Head {
+        unsafe { &*(self.mmap.as_ptr() as *const u8 as *const Head) }
+    }
+
+    /// Returns the user cache generation number.
+    pub fn user_cache_generation(&self) ->  usize {
+        self.head().user_cache_generation.load(Ordering::Acquire)
+    }
+
+    /// Increase the user cache generation number.
+    pub fn increase_user_cache_generation(&self) {
+        self.head()
+            .user_cache_generation
+            .fetch_add(1, Ordering::AcqRel);
+    }
+}
+
+/// The fast path opens an existing file.
+fn open_existing() ->  Result<Fd, nix::Error>  {
+      Fd::open(MEMCOM_FILE_PATH, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
+}
+
+/// Since we need to initialize the file, we also need a solid slow path where we create the file.
+/// In order to make sure the next user's `open()` vs `mmap()` race against our `truncate()` call,
+/// we create it in a temporary location and rotate it in place.
+fn create_new() ->  Result<Fd, Error>  {
+    // create a temporary file:
+    let temp_file_name = format!("{}.{}", MEMCOM_FILE_PATH, unsafe { libc::getpid() });
+    let fd = Fd::open(
+        temp_file_name.as_str(),
+        OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_RDWR | OFlag::O_CLOEXEC,
+        Mode::from_bits_truncate(0o660),
+    ).map_err(|err| {
+        format_err!(
+            "failed to create new in-memory communication file at {} - {}",
+            temp_file_name,
+            err
+        )
+    })?;
+
+    // let it be a page in size, it'll be initialized to zero by the kernel
+    nix::unistd::ftruncate(fd.as_raw_fd(), 4096)
+        .map_err(|err| format_err!("failed to set size of {} - {}", temp_file_name, err))?;
+
+    // if this is the pbs-daemon (running as root) rather than the proxy (running as backup user),
+    // make sure the backup user can access the file:
+    if let Ok(backup_user) = crate::backup::backup_user() {
+        match nix::unistd::fchown(fd.as_raw_fd(), None, Some(backup_user.gid)) {
+            Ok(()) =>  (),
+            Err(err) if err.is_errno(Errno::EPERM) =>  {
+                // we're not the daemon (root), so the file is already owned by the backup user
+            }
+            Err(err) =>  bail!(
+                "failed to set group to 'backup' for {} - {}",
+                temp_file_name,
+                err
+            ),
+        }
+    }
+
+    // rotate the file into place, but use `RENAME_NOREPLACE`, so in case 2 processes race against
+    // the initialization, the first one wins!
+    // TODO: nicer `renameat2()` wrapper in `proxmox::sys`?
+    let c_file_name = CString::new(temp_file_name.as_bytes()).unwrap();
+    let new_path = CString::new(MEMCOM_FILE_PATH).unwrap();
+      let rc = unsafe {
+          libc::renameat2(
+              -1,
+              c_file_name.as_ptr(),
+              -1,
+              new_path.as_ptr(),
+              libc::RENAME_NOREPLACE,
+          )
+      };
+    if rc == 0 {
+        return Ok(fd);
+    }
+    let err = io::Error::last_os_error();
+
+    // if another process has already raced ahead and created the file, let's just open theirs
+    // instead:
+    if err.kind() == io::ErrorKind::AlreadyExists {
+        // someone beat us to it:
+        drop(fd);
+        return open_existing().map_err(Error::from);
+    }
+    
+    // for any other errors, just bail out
+    bail!(
+        "failed to move file at {} into place at {} - {}",
+        temp_file_name,
+        MEMCOM_FILE_PATH,
+        err
+    );
+}
-- 
2.30.2




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI to manage openid realms.
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI " Dietmar Maurer
@ 2021-06-25 11:41   ` Wolfgang Bumiller
  0 siblings, 0 replies; 13+ messages in thread
From: Wolfgang Bumiller @ 2021-06-25 11:41 UTC (permalink / raw)
  To: Dietmar Maurer; +Cc: pbs-devel

just a minor cleanup:

On Fri, Jun 25, 2021 at 11:20:45AM +0200, Dietmar Maurer wrote:
> diff --git a/src/bin/proxmox_backup_manager/openid.rs b/src/bin/proxmox_backup_manager/openid.rs
> new file mode 100644
> index 00000000..13915339
> --- /dev/null
> +++ b/src/bin/proxmox_backup_manager/openid.rs
> @@ -0,0 +1,99 @@
> +use anyhow::Error;
> +use serde_json::Value;
> +
> +use proxmox::api::{api, cli::*, RpcEnvironment, ApiHandler};
> +
> +use proxmox_backup::{config, api2, api2::types::REALM_ID_SCHEMA};
> +
> +
> +#[api(
> +    input: {
> +        properties: {
> +            "output-format": {
> +                schema: OUTPUT_FORMAT,
> +                optional: true,
> +            },
> +        }
> +    }
> +)]
> +/// List configured OpenId realms
> +fn list_openid_realms(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
> +
> +    let output_format = get_output_format(&param);
> +
> +    let info = &api2::config::access::openid::API_METHOD_LIST_OPENID_REALMS;
> +    let mut data = match info.handler {
> +        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
> +        _ => unreachable!(),
> +    };
> +
> +    let options = default_table_format_options()
> +        .column(ColumnConfig::new("realm"))
> +        .column(ColumnConfig::new("issuer-url"))
> +        .column(ColumnConfig::new("comment"));
> +
> +    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
> +
> +    Ok(Value::Null)
> +}
> +#[api(
> +    input: {
> +        properties: {
> +            realm: {
> +                schema: REALM_ID_SCHEMA,
> +            },
> +            "output-format": {
> +                schema: OUTPUT_FORMAT,
> +                optional: true,
> +            },
> +        }
> +    }
> +)]
> +
> +/// Show OpenID realm configuration
> +fn show_openid_realm(param: Value, rpcenv: &mut dyn RpcEnvironment) -> Result<Value, Error> {
> +
> +    let output_format = get_output_format(&param);
> +
> +    let info = &api2::config::access::openid::API_METHOD_READ_OPENID_REALM;
> +    let mut data = match info.handler {
> +        ApiHandler::Sync(handler) => (handler)(param, info, rpcenv)?,
> +        _ => unreachable!(),
> +    };
> +
> +    let options = default_table_format_options();
> +    format_and_print_result_full(&mut data, &info.returns, &output_format, &options);
> +
> +    Ok(Value::Null)
> +}
> +
> +pub fn openid_commands() -> CommandLineInterface {
> +
> +    let cmd_def = CliCommandMap::new()
> +        .insert("list", CliCommand::new(&&API_METHOD_LIST_OPENID_REALMS))
> +        .insert("show", CliCommand::new(&&API_METHOD_SHOW_OPENID_REALM)
> +                .arg_param(&["realm"])
> +                .completion_cb("realm", config::domains::complete_openid_realm_name)
> +        )
> +        .insert("create",
> +                CliCommand::new(&api2::config::access::openid::API_METHOD_CREATE_OPENID_REALM)
> +                .arg_param(&["realm"])
> +                .arg_param(&["realm"])

^ this line is duplicated

> +                .completion_cb("realm", config::domains::complete_openid_realm_name)
> +        )
> +        .insert("update",
> +                CliCommand::new(&api2::config::access::openid::API_METHOD_UPDATE_OPENID_REALM)
> +                .arg_param(&["realm"])
> +                .arg_param(&["realm"])

^ this line is duplicated

> +                .completion_cb("realm", config::domains::complete_openid_realm_name)
> +        )
> +        .insert("delete",
> +                CliCommand::new(&api2::config::access::openid::API_METHOD_DELETE_OPENID_REALM)
> +                .arg_param(&["realm"])
> +                .arg_param(&["realm"])

^ this line is duplicated

> +                .completion_cb("realm", config::domains::complete_openid_realm_name)
> +        )
> +        ;
> +
> +       cmd_def.into()
> +}




^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms
  2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
                   ` (9 preceding siblings ...)
  2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 10/10] fix CachedUserInfo by using a shared memory version counter Dietmar Maurer
@ 2021-06-29  9:50 ` Fabian Grünbichler
  10 siblings, 0 replies; 13+ messages in thread
From: Fabian Grünbichler @ 2021-06-29  9:50 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion

seems to work okay in general (and looks okay code-wise), some missing 
pieces:
- proxmox-openid-rs prints some information that looks like debug (full 
  returned info from provider)
- user-attr in proxmox-backup like in PVE
- GUI: user adding/editing needs some adaptation for openid (e.g., removing a 
  user prints a warning about unknown realm, but removes it anyway, 
  adding a user has no realm selection and requires a password)
- GUI: realm adding/editing is missing altogether

the check_acl_path change could be split and folded into the patches 
that first use that ACL path, but no hard feelings there..

the last patch probably requires some in-depth review, I only took a 
cursory glance (and it could be ordered before the patch with the fixme 
that it fixes)

On June 25, 2021 11:20 am, Dietmar Maurer wrote:
> This implements OpenID connect realms using the new
> "proxmox-openid-rs" crate.
> 
> Note: The new src/tools/memcom.rs is from Wolfgang.
> 
> Changes since v2:
>    fix CachedUserInfo by using a shared memory version counter
> 
> Changes since v1:
> - really fix commit message of first patch
> - change api endpoints (/access/openid/{login|auth-url})
> - merged all api implementation patches
> 
> Changes since preview version (for Fabian):
> - fix commit message
> - reserve namen 'pam' and 'pbs'
> - fix 'make deb'
> 
> 
> Dietmar Maurer (10):
>   depend on proxmox-openid-rs
>   config: new domains.cfg to configure openid realm
>   check_acl_path: add /access/domains and /access/openid
>   add API to manage openid realms
>   cli: add CLI to manage openid realms.
>   implement new helper is_active_user_id()
>   cleanup user/token is_active() check
>   api: add openid redirect/login API
>   ui: implement OpenId login
>   fix CachedUserInfo by using a shared memory version counter
> 
>  Cargo.toml                               |   2 +
>  src/api2/access.rs                       |   4 +-
>  src/api2/access/domain.rs                |  18 ++
>  src/api2/access/openid.rs                | 190 ++++++++++++++++
>  src/api2/config/access/mod.rs            |   8 +-
>  src/api2/config/access/openid.rs         | 274 +++++++++++++++++++++++
>  src/bin/proxmox-backup-manager.rs        |   1 +
>  src/bin/proxmox_backup_manager/mod.rs    |   2 +
>  src/bin/proxmox_backup_manager/openid.rs |  99 ++++++++
>  src/config.rs                            |   1 +
>  src/config/acl.rs                        |   8 +-
>  src/config/cached_user_info.rs           |  48 ++--
>  src/config/domains.rs                    | 173 ++++++++++++++
>  src/config/user.rs                       |  38 ++++
>  src/tools.rs                             |   3 +
>  src/tools/memcom.rs                      | 159 +++++++++++++
>  www/Application.js                       |   8 +-
>  www/LoginView.js                         | 100 ++++++++-
>  www/Utils.js                             |   8 +
>  19 files changed, 1115 insertions(+), 29 deletions(-)
>  create mode 100644 src/api2/access/openid.rs
>  create mode 100644 src/api2/config/access/openid.rs
>  create mode 100644 src/bin/proxmox_backup_manager/openid.rs
>  create mode 100644 src/config/domains.rs
>  create mode 100644 src/tools/memcom.rs
> 
> -- 
> 2.30.2
> 
> 
> _______________________________________________
> pbs-devel mailing list
> pbs-devel@lists.proxmox.com
> https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel
> 
> 
> 




^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2021-06-29  9:50 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-25  9:20 [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 01/10] depend on proxmox-openid-rs Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 02/10] config: new domains.cfg to configure openid realm Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 03/10] check_acl_path: add /access/domains and /access/openid Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 04/10] add API to manage openid realms Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 05/10] cli: add CLI " Dietmar Maurer
2021-06-25 11:41   ` Wolfgang Bumiller
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 06/10] implement new helper is_active_user_id() Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 07/10] cleanup user/token is_active() check Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 08/10] api: add openid redirect/login API Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 09/10] ui: implement OpenId login Dietmar Maurer
2021-06-25  9:20 ` [pbs-devel] [PATCH proxmox-backup v3 10/10] fix CachedUserInfo by using a shared memory version counter Dietmar Maurer
2021-06-29  9:50 ` [pbs-devel] [PATCH proxmox-backup v3 00/10] OpenID connect realms Fabian Grünbichler

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal