redis_enterprise/
users.rs

1//! Users management for Redis Enterprise
2//!
3//! ## Overview
4//! - List and query resources
5//! - Create and update configurations
6//! - Monitor status and metrics
7//!
8//! Example
9//! ```no_run
10//! ```
11
12use crate::client::RestClient;
13use crate::error::Result;
14use serde::{Deserialize, Serialize};
15use serde_json::Value;
16use typed_builder::TypedBuilder;
17
18/// User information
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct User {
21    pub uid: u32,
22    /// User's email address (used as login identifier) - was incorrectly named 'username'
23    pub email: String,
24    /// User's display name
25    pub name: Option<String>,
26    /// User's role
27    pub role: String,
28    /// User status (e.g., "active")
29    pub status: Option<String>,
30    /// Authentication method (e.g., "regular")
31    pub auth_method: Option<String>,
32    /// Certificate subject line for certificate auth
33    pub certificate_subject_line: Option<String>,
34    /// Password issue date
35    pub password_issue_date: Option<String>,
36    /// Whether user receives email alerts
37    pub email_alerts: Option<bool>,
38    /// List of role UIDs
39    pub role_uids: Option<Vec<u32>>,
40    /// Database IDs for alerts
41    pub bdbs: Option<Vec<u32>>,
42    /// Alert for audit database connections
43    pub alert_audit_db_conns: Option<bool>,
44    /// Alert for BDB backup
45    pub alert_bdb_backup: Option<bool>,
46    /// Alert for BDB CRDT source syncer
47    pub alert_bdb_crdt_src_syncer: Option<bool>,
48    /// Password expiration duration in seconds
49    pub password_expiration_duration: Option<u32>,
50
51    #[serde(flatten)]
52    pub extra: Value,
53}
54
55/// Create user request
56///
57/// # Examples
58///
59/// ```rust,no_run
60/// use redis_enterprise::CreateUserRequest;
61///
62/// let request = CreateUserRequest::builder()
63///     .email("john.doe@example.com")
64///     .password("secure-password-123")
65///     .role("db_admin")
66///     .name("John Doe")
67///     .email_alerts(true)
68///     .build();
69/// ```
70#[derive(Debug, Serialize, TypedBuilder)]
71pub struct CreateUserRequest {
72    /// User's email address (required, used as login)
73    #[builder(setter(into))]
74    pub email: String,
75    /// User's password (required)
76    #[builder(setter(into))]
77    pub password: String,
78    /// User's role (required) - for RBAC-enabled clusters, use role_uids instead
79    #[builder(setter(into))]
80    pub role: String,
81    /// User's full name
82    #[serde(skip_serializing_if = "Option::is_none")]
83    #[builder(default, setter(into, strip_option))]
84    pub name: Option<String>,
85    /// Whether user should receive email alerts
86    #[serde(skip_serializing_if = "Option::is_none")]
87    #[builder(default, setter(strip_option))]
88    pub email_alerts: Option<bool>,
89    /// Database IDs for which the user should receive email alerts
90    #[serde(skip_serializing_if = "Option::is_none")]
91    #[builder(default, setter(strip_option))]
92    pub bdbs_email_alerts: Option<Vec<String>>,
93    /// Role IDs for RBAC-enabled clusters
94    #[serde(skip_serializing_if = "Option::is_none")]
95    #[builder(default, setter(strip_option))]
96    pub role_uids: Option<Vec<u32>>,
97    /// Authentication method (e.g., "regular")
98    #[serde(skip_serializing_if = "Option::is_none")]
99    #[builder(default, setter(into, strip_option))]
100    pub auth_method: Option<String>,
101}
102
103/// Update user request
104///
105/// # Examples
106///
107/// ```rust,no_run
108/// use redis_enterprise::UpdateUserRequest;
109///
110/// let request = UpdateUserRequest::builder()
111///     .password("new-secure-password")
112///     .email_alerts(false)
113///     .build();
114/// ```
115#[derive(Debug, Serialize, TypedBuilder)]
116pub struct UpdateUserRequest {
117    /// New password for the user
118    #[serde(skip_serializing_if = "Option::is_none")]
119    #[builder(default, setter(into, strip_option))]
120    pub password: Option<String>,
121    /// Update user's role
122    #[serde(skip_serializing_if = "Option::is_none")]
123    #[builder(default, setter(into, strip_option))]
124    pub role: Option<String>,
125    /// Update user's email address
126    #[serde(skip_serializing_if = "Option::is_none")]
127    #[builder(default, setter(into, strip_option))]
128    pub email: Option<String>,
129    /// Update user's full name
130    #[serde(skip_serializing_if = "Option::is_none")]
131    #[builder(default, setter(into, strip_option))]
132    pub name: Option<String>,
133    /// Update email alerts preference
134    #[serde(skip_serializing_if = "Option::is_none")]
135    #[builder(default, setter(strip_option))]
136    pub email_alerts: Option<bool>,
137    /// Update database IDs for email alerts
138    #[serde(skip_serializing_if = "Option::is_none")]
139    #[builder(default, setter(strip_option))]
140    pub bdbs_email_alerts: Option<Vec<String>>,
141    /// Update role IDs for RBAC-enabled clusters
142    #[serde(skip_serializing_if = "Option::is_none")]
143    #[builder(default, setter(strip_option))]
144    pub role_uids: Option<Vec<u32>>,
145    /// Update authentication method
146    #[serde(skip_serializing_if = "Option::is_none")]
147    #[builder(default, setter(into, strip_option))]
148    pub auth_method: Option<String>,
149}
150
151/// Role information
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct Role {
154    pub uid: u32,
155    pub name: String,
156    pub management: Option<String>,
157    pub data_access: Option<String>,
158
159    #[serde(flatten)]
160    pub extra: Value,
161}
162
163/// User handler for managing users
164pub struct UserHandler {
165    client: RestClient,
166}
167
168/// Alias for backwards compatibility and intuitive plural naming
169pub type UsersHandler = UserHandler;
170
171impl UserHandler {
172    pub fn new(client: RestClient) -> Self {
173        UserHandler { client }
174    }
175
176    /// List all users
177    pub async fn list(&self) -> Result<Vec<User>> {
178        self.client.get("/v1/users").await
179    }
180
181    /// Get specific user
182    pub async fn get(&self, uid: u32) -> Result<User> {
183        self.client.get(&format!("/v1/users/{}", uid)).await
184    }
185
186    /// Create new user
187    pub async fn create(&self, request: CreateUserRequest) -> Result<User> {
188        self.client.post("/v1/users", &request).await
189    }
190
191    /// Update user
192    pub async fn update(&self, uid: u32, request: UpdateUserRequest) -> Result<User> {
193        self.client
194            .put(&format!("/v1/users/{}", uid), &request)
195            .await
196    }
197
198    /// Delete user
199    pub async fn delete(&self, uid: u32) -> Result<()> {
200        self.client.delete(&format!("/v1/users/{}", uid)).await
201    }
202
203    /// Get permissions - GET /v1/users/permissions (raw)
204    pub async fn permissions(&self) -> Result<Value> {
205        self.client.get("/v1/users/permissions").await
206    }
207
208    /// Get permission detail - GET /v1/users/permissions/{perm} (raw)
209    pub async fn permission_detail(&self, perm: &str) -> Result<Value> {
210        self.client
211            .get(&format!("/v1/users/permissions/{}", perm))
212            .await
213    }
214
215    /// Authorize user (login) - POST /v1/users/authorize (raw)
216    pub async fn authorize(&self, body: AuthRequest) -> Result<AuthResponse> {
217        self.client.post("/v1/users/authorize", &body).await
218    }
219
220    /// Set password - POST /v1/users/password (raw)
221    pub async fn password_set(&self, body: PasswordSet) -> Result<()> {
222        self.client.post_action("/v1/users/password", &body).await
223    }
224
225    /// Update password - PUT /v1/users/password (raw)
226    pub async fn password_update(&self, body: PasswordUpdate) -> Result<()> {
227        self.client.put("/v1/users/password", &body).await
228    }
229
230    /// Delete password - DELETE /v1/users/password
231    pub async fn password_delete(&self) -> Result<()> {
232        self.client.delete("/v1/users/password").await
233    }
234
235    /// Refresh JWT - POST /v1/users/refresh_jwt (raw)
236    pub async fn refresh_jwt(&self, body: JwtRefreshRequest) -> Result<JwtRefreshResponse> {
237        self.client.post("/v1/users/refresh_jwt", &body).await
238    }
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct AuthRequest {
243    pub email: String,
244    pub password: String,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct AuthResponse {
249    pub jwt: String,
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub expires_at: Option<String>,
252    #[serde(flatten)]
253    pub extra: Value,
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct PasswordSet {
258    pub email: String,
259    pub password: String,
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct PasswordUpdate {
264    #[serde(skip_serializing_if = "Option::is_none")]
265    pub current_password: Option<String>,
266    pub new_password: String,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct JwtRefreshRequest {
271    pub jwt: String,
272}
273
274#[derive(Debug, Clone, Serialize, Deserialize)]
275pub struct JwtRefreshResponse {
276    pub jwt: String,
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub expires_at: Option<String>,
279}
280
281/// Role handler for managing roles
282pub struct RoleHandler {
283    client: RestClient,
284}
285
286impl RoleHandler {
287    pub fn new(client: RestClient) -> Self {
288        RoleHandler { client }
289    }
290
291    /// List all roles
292    pub async fn list(&self) -> Result<Vec<Role>> {
293        self.client.get("/v1/roles").await
294    }
295
296    /// Get specific role
297    pub async fn get(&self, uid: u32) -> Result<Role> {
298        self.client.get(&format!("/v1/roles/{}", uid)).await
299    }
300}