Skip to main content

casdoor_rust_sdk/
ldap.rs

1// Copyright 2026 The Casdoor Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use serde::{Deserialize, Serialize};
18
19use crate::client::{get_owner, Client, PostBody, Response};
20use crate::error::Result;
21use crate::serde_util::null_to_default;
22
23/// Ldap has the same definition as
24/// <https://github.com/casdoor/casdoor/blob/master/object/ldap.go>.
25#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase", default)]
27pub struct Ldap {
28    pub id: String,
29    pub owner: String,
30    pub created_time: String,
31
32    pub server_name: String,
33    pub host: String,
34    pub port: i32,
35    pub enable_ssl: bool,
36    pub allow_self_signed_cert: bool,
37    pub username: String,
38    pub password: String,
39    pub base_dn: String,
40    pub filter: String,
41    #[serde(deserialize_with = "null_to_default")]
42    pub filter_fields: Vec<String>,
43    pub default_group: String,
44    #[serde(deserialize_with = "null_to_default")]
45    pub default_groups: Vec<String>,
46    pub password_type: String,
47    #[serde(deserialize_with = "null_to_default")]
48    pub custom_attributes: HashMap<String, String>,
49
50    pub auto_sync: i32,
51    pub last_sync: String,
52    pub enable_groups: bool,
53    pub enable_password_reset: bool,
54}
55
56/// One user read from an LDAP server.
57#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase", default)]
59pub struct LdapUser {
60    pub uid_number: String,
61    pub uid: String,
62    pub cn: String,
63    pub gid_number: String,
64    pub uuid: String,
65    pub user_principal_name: String,
66    pub display_name: String,
67    #[serde(rename = "Mail")]
68    pub mail: String,
69    pub email: String,
70    #[serde(rename = "EmailAddress")]
71    pub email_address: String,
72    #[serde(rename = "TelephoneNumber")]
73    pub telephone_number: String,
74    pub mobile: String,
75    #[serde(rename = "MobileTelephoneNumber")]
76    pub mobile_telephone_number: String,
77    #[serde(rename = "RegisteredAddress")]
78    pub registered_address: String,
79    #[serde(rename = "PostalAddress")]
80    pub postal_address: String,
81    pub country: String,
82    pub country_name: String,
83
84    pub group_id: String,
85    pub address: String,
86    #[serde(deserialize_with = "null_to_default")]
87    pub member_of: Vec<String>,
88    #[serde(deserialize_with = "null_to_default")]
89    pub attributes: HashMap<String, String>,
90}
91
92/// The response of [`Client::get_ldap_users()`].
93#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
94#[serde(rename_all = "camelCase", default)]
95pub struct LdapUsersResponse {
96    #[serde(deserialize_with = "null_to_default")]
97    pub exist_uuids: Vec<String>,
98    #[serde(deserialize_with = "null_to_default")]
99    pub users: Vec<LdapUser>,
100}
101
102/// The response of [`Client::sync_ldap_users()`].
103#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
104#[serde(rename_all = "camelCase", default)]
105pub struct SyncLdapUsersResponse {
106    #[serde(deserialize_with = "null_to_default")]
107    pub exist: Vec<LdapUser>,
108    #[serde(deserialize_with = "null_to_default")]
109    pub failed: Vec<LdapUser>,
110}
111
112impl Client {
113    /// Get all the LDAP servers.
114    pub async fn get_ldaps(&self) -> Result<Vec<Ldap>> {
115        self.get_list("get-ldaps", &[("owner", &self.config.organization_name)])
116            .await
117    }
118
119    /// Get one LDAP server by ID.
120    pub async fn get_ldap(&self, id: &str) -> Result<Option<Ldap>> {
121        self.get_object("get-ldap", &[("id", &self.get_id(id))])
122            .await
123    }
124
125    /// Add an LDAP server.
126    pub async fn add_ldap(&self, ldap: &Ldap) -> Result<bool> {
127        Ok(self.modify_ldap("add-ldap", ldap, &[]).await?.1)
128    }
129
130    /// Update an LDAP server.
131    pub async fn update_ldap(&self, ldap: &Ldap) -> Result<bool> {
132        Ok(self.modify_ldap("update-ldap", ldap, &[]).await?.1)
133    }
134
135    /// Delete an LDAP server.
136    pub async fn delete_ldap(&self, ldap: &Ldap) -> Result<bool> {
137        Ok(self.modify_ldap("delete-ldap", ldap, &[]).await?.1)
138    }
139
140    /// Get the users of an LDAP server, together with the UUIDs that already exist in
141    /// Casdoor.
142    pub async fn get_ldap_users(&self, id: &str) -> Result<Option<LdapUsersResponse>> {
143        self.get_object("get-ldap-users", &[("id", &self.get_id(id))])
144            .await
145    }
146
147    /// Sync the given LDAP users into Casdoor.
148    pub async fn sync_ldap_users(
149        &self,
150        id: &str,
151        users: &[LdapUser],
152    ) -> Result<Option<SyncLdapUsersResponse>> {
153        let post_bytes = serde_json::to_vec(users)?;
154
155        let response = self
156            .do_post(
157                "sync-ldap-users",
158                &[("id", &self.get_id(id))],
159                PostBody::Raw(post_bytes),
160            )
161            .await?;
162
163        if response.data.is_null() {
164            return Ok(None);
165        }
166
167        Ok(Some(serde_json::from_value(response.data)?))
168    }
169
170    /// Read the users of an LDAP server and sync them all into Casdoor.
171    pub async fn sync_ldap_users_from_server(
172        &self,
173        id: &str,
174    ) -> Result<Option<SyncLdapUsersResponse>> {
175        let users = self
176            .get_ldap_users(id)
177            .await?
178            .map(|response| response.users)
179            .unwrap_or_default();
180
181        self.sync_ldap_users(id, &users).await
182    }
183
184    async fn modify_ldap(
185        &self,
186        action: &str,
187        ldap: &Ldap,
188        columns: &[&str],
189    ) -> Result<(Response, bool)> {
190        let mut ldap = ldap.clone();
191        ldap.owner = get_owner(&ldap.owner, &self.config.organization_name);
192
193        let id = format!("{}/{}", ldap.owner, ldap.id);
194        self.modify_object(action, &id, &ldap, columns).await
195    }
196}