mysqladm/core/database_privileges/
cli.rs1use super::diff::{DatabasePrivilegeChange, DatabasePrivilegeRowDiff};
5use crate::core::types::{MySQLDatabase, MySQLUser};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum DatabasePrivilegeEditEntryType {
11 Add,
12 Set,
13 Remove,
14}
15
16#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct DatabasePrivilegeEditEntry {
23 pub database: Option<MySQLDatabase>,
24 pub user: MySQLUser,
25 pub type_: DatabasePrivilegeEditEntryType,
26 pub privileges: Vec<String>,
27}
28
29impl DatabasePrivilegeEditEntry {
30 pub fn parse_from_str(arg: &str) -> anyhow::Result<DatabasePrivilegeEditEntry> {
43 let parts: Vec<&str> = arg.split(':').collect();
44 if parts.len() < 2 || parts.len() > 3 {
45 anyhow::bail!("Invalid privilege edit entry format: {}", arg);
46 }
47
48 let (database, user, user_privs) = if parts.len() == 3 {
49 (Some(parts[0].to_string()), parts[1].to_string(), parts[2])
50 } else {
51 (None, parts[0].to_string(), parts[1])
52 };
53
54 if user.is_empty() {
55 anyhow::bail!("Username cannot be empty in privilege edit entry: {}", arg);
56 }
57
58 let (edit_type, privs_str) = if let Some(privs_str) = user_privs.strip_prefix('+') {
59 (DatabasePrivilegeEditEntryType::Add, privs_str)
60 } else if let Some(privs_str) = user_privs.strip_prefix('-') {
61 (DatabasePrivilegeEditEntryType::Remove, privs_str)
62 } else {
63 (DatabasePrivilegeEditEntryType::Set, user_privs)
64 };
65
66 let privileges: Vec<String> = privs_str.chars().map(|c| c.to_string()).collect();
67 if privileges.iter().any(|c| !"siudcDaAItlrA".contains(c)) {
68 let invalid_chars: String = privileges
69 .iter()
70 .filter(|c| !"siudcDaAItlrA".contains(c.as_str()))
71 .cloned()
72 .collect();
73 anyhow::bail!(
74 "Invalid character(s) in privilege edit entry: {}",
75 invalid_chars
76 );
77 }
78
79 Ok(DatabasePrivilegeEditEntry {
80 database: database.map(MySQLDatabase::from),
81 user: MySQLUser::from(user),
82 type_: edit_type,
83 privileges,
84 })
85 }
86
87 pub fn as_database_privileges_diff(
88 &self,
89 external_database_name: Option<&MySQLDatabase>,
90 ) -> anyhow::Result<DatabasePrivilegeRowDiff> {
91 let database = match self.database.as_ref() {
92 Some(db) => db.clone(),
93 None => {
94 if let Some(external_db) = external_database_name {
95 external_db.clone()
96 } else {
97 anyhow::bail!(
98 "Database name must be specified either in the privilege edit entry or as an external argument."
99 );
100 }
101 }
102 };
103 let mut diff;
104 match self.type_ {
105 DatabasePrivilegeEditEntryType::Set => {
106 diff = DatabasePrivilegeRowDiff {
107 db: database,
108 user: self.user.clone(),
109 select_priv: Some(DatabasePrivilegeChange::YesToNo),
110 insert_priv: Some(DatabasePrivilegeChange::YesToNo),
111 update_priv: Some(DatabasePrivilegeChange::YesToNo),
112 delete_priv: Some(DatabasePrivilegeChange::YesToNo),
113 create_priv: Some(DatabasePrivilegeChange::YesToNo),
114 drop_priv: Some(DatabasePrivilegeChange::YesToNo),
115 alter_priv: Some(DatabasePrivilegeChange::YesToNo),
116 index_priv: Some(DatabasePrivilegeChange::YesToNo),
117 create_tmp_table_priv: Some(DatabasePrivilegeChange::YesToNo),
118 lock_tables_priv: Some(DatabasePrivilegeChange::YesToNo),
119 references_priv: Some(DatabasePrivilegeChange::YesToNo),
120 };
121 for priv_char in &self.privileges {
122 match priv_char.as_str() {
123 "s" => diff.select_priv = Some(DatabasePrivilegeChange::NoToYes),
124 "i" => diff.insert_priv = Some(DatabasePrivilegeChange::NoToYes),
125 "u" => diff.update_priv = Some(DatabasePrivilegeChange::NoToYes),
126 "d" => diff.delete_priv = Some(DatabasePrivilegeChange::NoToYes),
127 "c" => diff.create_priv = Some(DatabasePrivilegeChange::NoToYes),
128 "D" => diff.drop_priv = Some(DatabasePrivilegeChange::NoToYes),
129 "a" => diff.alter_priv = Some(DatabasePrivilegeChange::NoToYes),
130 "I" => diff.index_priv = Some(DatabasePrivilegeChange::NoToYes),
131 "t" => diff.create_tmp_table_priv = Some(DatabasePrivilegeChange::NoToYes),
132 "l" => diff.lock_tables_priv = Some(DatabasePrivilegeChange::NoToYes),
133 "r" => diff.references_priv = Some(DatabasePrivilegeChange::NoToYes),
134 "A" => {
135 diff.select_priv = Some(DatabasePrivilegeChange::NoToYes);
136 diff.insert_priv = Some(DatabasePrivilegeChange::NoToYes);
137 diff.update_priv = Some(DatabasePrivilegeChange::NoToYes);
138 diff.delete_priv = Some(DatabasePrivilegeChange::NoToYes);
139 diff.create_priv = Some(DatabasePrivilegeChange::NoToYes);
140 diff.drop_priv = Some(DatabasePrivilegeChange::NoToYes);
141 diff.alter_priv = Some(DatabasePrivilegeChange::NoToYes);
142 diff.index_priv = Some(DatabasePrivilegeChange::NoToYes);
143 diff.create_tmp_table_priv = Some(DatabasePrivilegeChange::NoToYes);
144 diff.lock_tables_priv = Some(DatabasePrivilegeChange::NoToYes);
145 diff.references_priv = Some(DatabasePrivilegeChange::NoToYes);
146 }
147 _ => unreachable!(),
148 }
149 }
150 }
151 DatabasePrivilegeEditEntryType::Add | DatabasePrivilegeEditEntryType::Remove => {
152 diff = DatabasePrivilegeRowDiff {
153 db: database,
154 user: self.user.clone(),
155 select_priv: None,
156 insert_priv: None,
157 update_priv: None,
158 delete_priv: None,
159 create_priv: None,
160 drop_priv: None,
161 alter_priv: None,
162 index_priv: None,
163 create_tmp_table_priv: None,
164 lock_tables_priv: None,
165 references_priv: None,
166 };
167 let value = match self.type_ {
168 DatabasePrivilegeEditEntryType::Add => DatabasePrivilegeChange::NoToYes,
169 DatabasePrivilegeEditEntryType::Remove => DatabasePrivilegeChange::YesToNo,
170 _ => unreachable!(),
171 };
172 for priv_char in &self.privileges {
173 match priv_char.as_str() {
174 "s" => diff.select_priv = Some(value),
175 "i" => diff.insert_priv = Some(value),
176 "u" => diff.update_priv = Some(value),
177 "d" => diff.delete_priv = Some(value),
178 "c" => diff.create_priv = Some(value),
179 "D" => diff.drop_priv = Some(value),
180 "a" => diff.alter_priv = Some(value),
181 "I" => diff.index_priv = Some(value),
182 "t" => diff.create_tmp_table_priv = Some(value),
183 "l" => diff.lock_tables_priv = Some(value),
184 "r" => diff.references_priv = Some(value),
185 "A" => {
186 diff.select_priv = Some(value);
187 diff.insert_priv = Some(value);
188 diff.update_priv = Some(value);
189 diff.delete_priv = Some(value);
190 diff.create_priv = Some(value);
191 diff.drop_priv = Some(value);
192 diff.alter_priv = Some(value);
193 diff.index_priv = Some(value);
194 diff.create_tmp_table_priv = Some(value);
195 diff.lock_tables_priv = Some(value);
196 diff.references_priv = Some(value);
197 }
198 _ => unreachable!(),
199 }
200 }
201 }
202 }
203
204 Ok(diff)
205 }
206}
207
208impl std::fmt::Display for DatabasePrivilegeEditEntry {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 if let Some(db) = &self.database {
211 write!(f, "{}:, ", db)?;
212 }
213 write!(f, "{}: ", self.user)?;
214 match self.type_ {
215 DatabasePrivilegeEditEntryType::Add => write!(f, "+")?,
216 DatabasePrivilegeEditEntryType::Set => {}
217 DatabasePrivilegeEditEntryType::Remove => write!(f, "-")?,
218 }
219 for priv_char in &self.privileges {
220 write!(f, "{}", priv_char)?;
221 }
222
223 Ok(())
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_cli_arg_parse_set_db_user_all() {
233 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:A");
234 assert_eq!(
235 result.ok(),
236 Some(DatabasePrivilegeEditEntry {
237 database: Some("db".into()),
238 user: "user".into(),
239 type_: DatabasePrivilegeEditEntryType::Set,
240 privileges: vec!["A".into()],
241 })
242 );
243 }
244
245 #[test]
246 fn test_cli_arg_parse_set_db_user_none() {
247 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:");
248 assert_eq!(
249 result.ok(),
250 Some(DatabasePrivilegeEditEntry {
251 database: Some("db".into()),
252 user: "user".into(),
253 type_: DatabasePrivilegeEditEntryType::Set,
254 privileges: vec![],
255 })
256 );
257 }
258
259 #[test]
260 fn test_cli_arg_parse_set_db_user_misc() {
261 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:siud");
262 assert_eq!(
263 result.ok(),
264 Some(DatabasePrivilegeEditEntry {
265 database: Some("db".into()),
266 user: "user".into(),
267 type_: DatabasePrivilegeEditEntryType::Set,
268 privileges: vec!["s".into(), "i".into(), "u".into(), "d".into()],
269 })
270 );
271 }
272
273 #[test]
274 fn test_cli_arg_parse_set_user_nonexistent_misc() {
275 let result = DatabasePrivilegeEditEntry::parse_from_str("user:siud");
276 assert_eq!(
277 result.ok(),
278 Some(DatabasePrivilegeEditEntry {
279 database: None,
280 user: "user".into(),
281 type_: DatabasePrivilegeEditEntryType::Set,
282 privileges: vec!["s".into(), "i".into(), "u".into(), "d".into()],
283 }),
284 );
285 }
286
287 #[test]
288 fn test_cli_arg_parse_set_db_user_nonexistent_privilege() {
289 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:F");
290 assert!(result.is_err());
291 }
292
293 #[test]
294 fn test_cli_arg_parse_set_user_empty_string() {
295 let result = DatabasePrivilegeEditEntry::parse_from_str("::");
296 assert!(result.is_err());
297 }
298
299 #[test]
300 fn test_cli_arg_parse_set_db_user_empty_string() {
301 let result = DatabasePrivilegeEditEntry::parse_from_str("db::");
302 assert!(result.is_err());
303 }
304
305 #[test]
306 fn test_cli_arg_parse_add_db_user_misc() {
307 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:+siud");
308 assert_eq!(
309 result.ok(),
310 Some(DatabasePrivilegeEditEntry {
311 database: Some("db".into()),
312 user: "user".into(),
313 type_: DatabasePrivilegeEditEntryType::Add,
314 privileges: vec!["s".into(), "i".into(), "u".into(), "d".into()],
315 })
316 );
317 }
318
319 #[test]
320 fn test_cli_arg_parse_remove_db_user_misc() {
321 let result = DatabasePrivilegeEditEntry::parse_from_str("db:user:-siud");
322 assert_eq!(
323 result.ok(),
324 Some(DatabasePrivilegeEditEntry {
325 database: Some("db".into()),
326 user: "user".into(),
327 type_: DatabasePrivilegeEditEntryType::Remove,
328 privileges: vec!["s".into(), "i".into(), "u".into(), "d".into()],
329 }),
330 );
331 }
332}