[pbs-devel] [PATCH proxmox-backup v2 09/15] realm sync: add sync job for AD realms

Christoph Heiss c.heiss at proxmox.com
Wed Aug 16 16:47:39 CEST 2023


Basically just a thin wrapper over the existing LDAP-based realm sync
job, which retrieves the appropriate config and sets the correct user
attributes.

Signed-off-by: Christoph Heiss <c.heiss at proxmox.com>
---
Changes v1 -> v2:
  * Implement `Display` + `FromStr` for `RealmType` using the
    serde_plain crate by deriving them. Previously a separate patch, but
    now folded into this as it was reduced to a two-liner due to this
    change.
  * Fix clippy deref warning

 pbs-api-types/src/lib.rs     |  5 +++
 src/api2/access/domain.rs    | 18 ++++++++--
 src/server/realm_sync_job.rs | 69 ++++++++++++++++++++++++++++++++----
 3 files changed, 84 insertions(+), 8 deletions(-)

diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs
index d2bce842..b1ac3c9d 100644
--- a/pbs-api-types/src/lib.rs
+++ b/pbs-api-types/src/lib.rs
@@ -509,8 +509,13 @@ pub enum RealmType {
     OpenId,
     /// An LDAP realm
     Ldap,
+    /// An Active Directory (AD) realm
+    Ad,
 }

+serde_plain::derive_display_from_serialize!(RealmType);
+serde_plain::derive_fromstr_from_deserialize!(RealmType);
+
 #[api(
     properties: {
         realm: {
diff --git a/src/api2/access/domain.rs b/src/api2/access/domain.rs
index 31aa62bc..8f8eebda 100644
--- a/src/api2/access/domain.rs
+++ b/src/api2/access/domain.rs
@@ -1,13 +1,14 @@
 //! List Authentication domains/realms

-use anyhow::{format_err, Error};
+use anyhow::{bail, format_err, Error};
 use serde_json::{json, Value};

 use proxmox_router::{Permission, Router, RpcEnvironment, RpcEnvironmentType, SubdirMap};
 use proxmox_schema::api;

 use pbs_api_types::{
-    Authid, BasicRealmInfo, Realm, PRIV_PERMISSIONS_MODIFY, REMOVE_VANISHED_SCHEMA, UPID_SCHEMA,
+    Authid, BasicRealmInfo, Realm, RealmRef, RealmType, PRIV_PERMISSIONS_MODIFY,
+    REMOVE_VANISHED_SCHEMA, UPID_SCHEMA,
 };

 use crate::server::jobstate::Job;
@@ -102,6 +103,7 @@ pub fn sync_realm(
     let upid_str = crate::server::do_realm_sync_job(
         job,
         realm.clone(),
+        realm_type_from_name(&realm)?,
         &auth_id,
         None,
         to_stdout,
@@ -120,6 +122,18 @@ pub fn sync_realm(
     Ok(json!(upid_str))
 }

+fn realm_type_from_name(realm: &RealmRef) -> Result<RealmType, Error> {
+    let config = pbs_config::domains::config()?.0;
+
+    for (name, (section_type, _)) in config.sections.iter() {
+        if name == realm.as_str() {
+            return Ok(section_type.parse()?);
+        }
+    }
+
+    bail!("unable to find realm {realm}")
+}
+
 const SYNC_ROUTER: Router = Router::new().post(&API_METHOD_SYNC_REALM);
 const SYNC_SUBDIRS: SubdirMap = &[("sync", &SYNC_ROUTER)];

diff --git a/src/server/realm_sync_job.rs b/src/server/realm_sync_job.rs
index 5a7bcf0b..de8124d7 100644
--- a/src/server/realm_sync_job.rs
+++ b/src/server/realm_sync_job.rs
@@ -10,9 +10,9 @@ use proxmox_sys::{task_log, task_warn};
 use std::{collections::HashSet, sync::Arc};

 use pbs_api_types::{
-    ApiToken, Authid, LdapRealmConfig, Realm, RemoveVanished, SyncAttributes as LdapSyncAttributes,
-    SyncDefaultsOptions, User, Userid, EMAIL_SCHEMA, FIRST_NAME_SCHEMA, LAST_NAME_SCHEMA,
-    REMOVE_VANISHED_ARRAY, USER_CLASSES_ARRAY,
+    AdRealmConfig, ApiToken, Authid, LdapRealmConfig, Realm, RealmType, RemoveVanished,
+    SyncAttributes as LdapSyncAttributes, SyncDefaultsOptions, User, Userid, EMAIL_SCHEMA,
+    FIRST_NAME_SCHEMA, LAST_NAME_SCHEMA, REMOVE_VANISHED_ARRAY, USER_CLASSES_ARRAY,
 };

 use crate::{auth, server::jobstate::Job};
@@ -22,6 +22,7 @@ use crate::{auth, server::jobstate::Job};
 pub fn do_realm_sync_job(
     mut job: Job,
     realm: Realm,
+    realm_type: RealmType,
     auth_id: &Authid,
     _schedule: Option<String>,
     to_stdout: bool,
@@ -46,8 +47,19 @@ pub fn do_realm_sync_job(
             };

             async move {
-                let sync_job = LdapRealmSyncJob::new(worker, realm, &override_settings, dry_run)?;
-                sync_job.sync().await
+                match realm_type {
+                    RealmType::Ldap => {
+                        LdapRealmSyncJob::new(worker, realm, &override_settings, dry_run)?
+                            .sync()
+                            .await
+                    }
+                    RealmType::Ad => {
+                        AdRealmSyncJob::new(worker, realm, &override_settings, dry_run)?
+                            .sync()
+                            .await
+                    }
+                    _ => bail!("cannot sync realm {realm} of type {realm_type}"),
+                }
             }
         },
     )?;
@@ -55,6 +67,51 @@ pub fn do_realm_sync_job(
     Ok(upid_str)
 }

+/// Implementation for syncing Active Directory realms. Merely a thin wrapper over
+/// `LdapRealmSyncJob`, as AD is just LDAP with some special requirements.
+struct AdRealmSyncJob(LdapRealmSyncJob);
+
+impl AdRealmSyncJob {
+    fn new(
+        worker: Arc<WorkerTask>,
+        realm: Realm,
+        override_settings: &GeneralSyncSettingsOverride,
+        dry_run: bool,
+    ) -> Result<Self, Error> {
+        let (domains, _digest) = pbs_config::domains::config()?;
+        let config = if let Ok(config) = domains.lookup::<AdRealmConfig>("ad", realm.as_str()) {
+            config
+        } else {
+            bail!("unknown Active Directory realm '{}'", realm.as_str());
+        };
+
+        let sync_settings = GeneralSyncSettings::default()
+            .apply_config(config.sync_defaults_options.as_deref())?
+            .apply_override(override_settings)?;
+        let sync_attributes = LdapSyncSettings::new(
+            "sAMAccountName",
+            config.sync_attributes.as_deref(),
+            config.user_classes.as_deref(),
+            config.filter.as_deref(),
+        )?;
+
+        let ldap_config = auth::AdAuthenticator::api_type_to_config(&config)?;
+
+        Ok(Self(LdapRealmSyncJob {
+            worker,
+            realm,
+            general_sync_settings: sync_settings,
+            ldap_sync_settings: sync_attributes,
+            ldap_config,
+            dry_run,
+        }))
+    }
+
+    async fn sync(&self) -> Result<(), Error> {
+        self.0.sync().await
+    }
+}
+
 /// Implemenation for syncing LDAP realms
 struct LdapRealmSyncJob {
     worker: Arc<WorkerTask>,
@@ -77,7 +134,7 @@ impl LdapRealmSyncJob {
         let config = if let Ok(config) = domains.lookup::<LdapRealmConfig>("ldap", realm.as_str()) {
             config
         } else {
-            bail!("unknown realm '{}'", realm.as_str());
+            bail!("unknown LDAP realm '{}'", realm.as_str());
         };

         let sync_settings = GeneralSyncSettings::default()
--
2.41.0






More information about the pbs-devel mailing list