risingwave_frontend/handler/
alter_user.rs1use std::collections::HashSet;
16
17use pgwire::pg_response::StatementType;
18use risingwave_common::catalog::{DEFAULT_SUPER_USER_FOR_ADMIN, is_reserved_admin_user};
19use risingwave_pb::user::UserInfo;
20use risingwave_pb::user::update_user_request::UpdateField;
21use risingwave_sqlparser::ast::{AlterUserStatement, ObjectName, UserOption, UserOptions};
22
23use super::RwPgResponse;
24use crate::binder::Binder;
25use crate::catalog::CatalogError;
26use crate::error::ErrorCode::{self, InternalError, PermissionDenied};
27use crate::error::Result;
28use crate::handler::HandlerArgs;
29use crate::user::user_authentication::{
30 OAUTH_ISSUER_KEY, OAUTH_JWKS_URL_KEY, build_oauth_info, encrypted_password,
31};
32use crate::user::user_catalog::UserCatalog;
33
34fn alter_prost_user_info(
35 mut user_info: UserInfo,
36 options: &UserOptions,
37 session_user: &UserCatalog,
38) -> Result<(UserInfo, Vec<UpdateField>, Vec<String>)> {
39 let change_self_password_only = session_user.id == user_info.id
40 && options.0.len() == 1
41 && matches!(
42 &options.0[0],
43 UserOption::EncryptedPassword(_) | UserOption::Password(_) | UserOption::OAuth(_)
44 );
45 let require_admin = user_info.is_admin
46 || options
47 .0
48 .iter()
49 .any(|option| matches!(option, UserOption::Admin | UserOption::NoAdmin));
50 if !change_self_password_only && require_admin && !session_user.is_admin {
51 return Err(PermissionDenied(
52 format!("{} cannot be altered with admin option", user_info.name).to_owned(),
53 )
54 .into());
55 }
56
57 if !session_user.is_super {
58 let require_super = user_info.is_super
59 || options
60 .0
61 .iter()
62 .any(|option| matches!(option, UserOption::SuperUser | UserOption::NoSuperUser));
63 if require_super {
64 return Err(PermissionDenied(
65 "must be superuser to alter superuser roles or change superuser attribute"
66 .to_owned(),
67 )
68 .into());
69 }
70
71 if !session_user.can_create_user && !change_self_password_only {
72 return Err(PermissionDenied("permission denied to alter user".to_owned()).into());
73 }
74 }
75
76 let mut update_fields = HashSet::new();
77 let mut notices = vec![];
78
79 for option in &options.0 {
80 match option {
81 UserOption::SuperUser => {
82 user_info.is_super = true;
83 update_fields.insert(UpdateField::Super);
84 }
85 UserOption::NoSuperUser => {
86 user_info.is_super = false;
87 update_fields.insert(UpdateField::Super);
88 }
89 UserOption::CreateDB => {
90 user_info.can_create_db = true;
91 update_fields.insert(UpdateField::CreateDb);
92 }
93 UserOption::NoCreateDB => {
94 user_info.can_create_db = false;
95 update_fields.insert(UpdateField::CreateDb);
96 }
97 UserOption::CreateUser => {
98 user_info.can_create_user = true;
99 update_fields.insert(UpdateField::CreateUser);
100 }
101 UserOption::NoCreateUser => {
102 user_info.can_create_user = false;
103 update_fields.insert(UpdateField::CreateUser);
104 }
105 UserOption::Login => {
106 user_info.can_login = true;
107 update_fields.insert(UpdateField::Login);
108 }
109 UserOption::NoLogin => {
110 user_info.can_login = false;
111 update_fields.insert(UpdateField::Login);
112 }
113 UserOption::Admin => {
114 user_info.is_admin = true;
115 update_fields.insert(UpdateField::Admin);
116 }
117 UserOption::NoAdmin => {
118 user_info.is_admin = false;
119 update_fields.insert(UpdateField::Admin);
120 }
121 UserOption::EncryptedPassword(p) => {
122 if !p.0.is_empty() {
123 user_info.auth_info = encrypted_password(&user_info.name, &p.0);
124 } else {
125 user_info.auth_info = None;
126 notices
127 .push("empty string is not a valid password, clearing password".to_owned());
128 };
129 update_fields.insert(UpdateField::AuthInfo);
130 }
131 UserOption::Password(opt) => {
132 if let Some(password) = opt
133 && !password.0.is_empty()
134 {
135 user_info.auth_info = encrypted_password(&user_info.name, &password.0);
136 } else {
137 user_info.auth_info = None;
138 notices
139 .push("empty string is not a valid password, clearing password".to_owned());
140 }
141 update_fields.insert(UpdateField::AuthInfo);
142 }
143 UserOption::OAuth(options) => {
144 let auth_info = build_oauth_info(options).ok_or_else(|| {
145 ErrorCode::InvalidParameterValue(format!(
146 "{} and {} must be provided",
147 OAUTH_JWKS_URL_KEY, OAUTH_ISSUER_KEY
148 ))
149 })?;
150 user_info.auth_info = Some(auth_info);
151 update_fields.insert(UpdateField::AuthInfo);
152 }
153 }
154 }
155
156 if user_info.is_admin && !user_info.is_super {
157 user_info.is_super = true;
158 update_fields.insert(UpdateField::Super);
159 if options
160 .0
161 .iter()
162 .any(|option| matches!(option, UserOption::NoSuperUser))
163 {
164 notices.push("admin users must remain superusers, ignoring NOSUPERUSER".to_owned());
165 }
166 }
167
168 Ok((user_info, update_fields.into_iter().collect(), notices))
169}
170
171fn alter_rename_prost_user_info(
172 mut user_info: UserInfo,
173 new_name: ObjectName,
174 session_user: &UserCatalog,
175) -> Result<(UserInfo, Vec<UpdateField>)> {
176 if session_user.id == user_info.id {
177 return Err(InternalError("session user cannot be renamed".to_owned()).into());
178 }
179
180 if !session_user.is_super {
181 return Err(PermissionDenied("must be superuser to rename users".to_owned()).into());
182 }
183
184 let new_name = Binder::resolve_user_name(new_name)?;
185 if is_reserved_admin_user(&new_name) {
186 return Err(PermissionDenied(
187 format!("{} is reserved for admin", DEFAULT_SUPER_USER_FOR_ADMIN).to_owned(),
188 )
189 .into());
190 }
191 if is_reserved_admin_user(&user_info.name) {
192 return Err(PermissionDenied(
193 format!("{} cannot be renamed", DEFAULT_SUPER_USER_FOR_ADMIN).to_owned(),
194 )
195 .into());
196 }
197
198 user_info.name = new_name;
199 user_info.auth_info = None;
200 Ok((user_info, vec![UpdateField::Rename, UpdateField::AuthInfo]))
201}
202
203pub async fn handle_alter_user(
204 handler_args: HandlerArgs,
205 stmt: AlterUserStatement,
206) -> Result<RwPgResponse> {
207 let session = handler_args.session;
208 let (user_info, update_fields, notices) = {
209 let user_name = Binder::resolve_user_name(stmt.user_name.clone())?;
210 let user_reader = session.env().user_info_reader().read_guard();
211
212 let old_info = user_reader
213 .get_user_by_name(&user_name)
214 .ok_or(CatalogError::NotFound("user", user_name))?
215 .to_prost();
216
217 let session_user = user_reader
218 .get_user_by_name(&session.user_name())
219 .ok_or_else(|| CatalogError::NotFound("user", session.user_name().to_owned()))?;
220
221 match stmt.mode {
222 risingwave_sqlparser::ast::AlterUserMode::Options(options) => {
223 alter_prost_user_info(old_info, &options, session_user)?
224 }
225 risingwave_sqlparser::ast::AlterUserMode::Rename(new_name) => {
226 let (user_info, fields) =
227 alter_rename_prost_user_info(old_info, new_name, session_user)?;
228 (user_info, fields, vec![])
229 }
230 }
231 };
232
233 let user_info_writer = session.user_info_writer()?;
234 user_info_writer
235 .update_user(user_info, update_fields)
236 .await?;
237 let mut response_builder = RwPgResponse::builder(StatementType::UPDATE_USER);
238 for notice in notices {
239 response_builder = response_builder.notice(notice);
240 }
241 Ok(response_builder.into())
242}
243
244#[cfg(test)]
245mod tests {
246 use std::collections::HashMap;
247
248 use risingwave_common::catalog::{
249 DEFAULT_DATABASE_NAME, DEFAULT_SUPER_USER_FOR_ADMIN, DEFAULT_SUPER_USER_FOR_ADMIN_ID,
250 };
251 use risingwave_pb::user::AuthInfo;
252 use risingwave_pb::user::auth_info::EncryptionType;
253
254 use crate::test_utils::LocalFrontend;
255
256 #[tokio::test]
257 async fn test_alter_user() {
258 let frontend = LocalFrontend::new(Default::default()).await;
259 let session = frontend.session_ref();
260 let user_info_reader = session.env().user_info_reader();
261
262 frontend.run_sql("CREATE USER userB WITH SUPERUSER NOCREATEDB PASSWORD 'md5827ccb0eea8a706c4c34a16891f84e7b'").await.unwrap();
263 frontend
264 .run_sql("ALTER USER userB RENAME TO user")
265 .await
266 .unwrap();
267 assert!(
268 user_info_reader
269 .read_guard()
270 .get_user_by_name("userB")
271 .is_none()
272 );
273 assert!(
274 user_info_reader
275 .read_guard()
276 .get_user_by_name("user")
277 .is_some()
278 );
279
280 frontend.run_sql("ALTER USER user WITH NOSUPERUSER CREATEDB PASSWORD 'md59f2fa6a30871a92249bdd2f1eeee4ef6'").await.unwrap();
281
282 let user_info = user_info_reader
283 .read_guard()
284 .get_user_by_name("user")
285 .cloned()
286 .unwrap();
287 assert!(!user_info.is_super);
288 assert!(user_info.can_create_db);
289 assert_eq!(
290 user_info.auth_info,
291 Some(AuthInfo {
292 encryption_type: EncryptionType::Md5 as i32,
293 encrypted_value: b"9f2fa6a30871a92249bdd2f1eeee4ef6".to_vec(),
294 metadata: HashMap::new(),
295 })
296 );
297 }
298
299 #[tokio::test]
300 async fn test_alter_admin_user() {
301 let frontend = LocalFrontend::new(Default::default()).await;
302 let session = frontend.session_ref();
303 let user_info_reader = session.env().user_info_reader();
304
305 frontend
307 .run_user_sql(
308 "CREATE USER admin_user WITH ADMIN",
309 DEFAULT_DATABASE_NAME.to_owned(),
310 DEFAULT_SUPER_USER_FOR_ADMIN.to_owned(),
311 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
312 )
313 .await
314 .unwrap();
315
316 let admin_user = user_info_reader
317 .read_guard()
318 .get_user_by_name("admin_user")
319 .cloned()
320 .unwrap();
321
322 assert!(admin_user.is_admin);
324 assert!(admin_user.is_super);
325
326 frontend
328 .run_user_sql(
329 "ALTER USER admin_user WITH NOSUPERUSER",
330 DEFAULT_DATABASE_NAME.to_owned(),
331 DEFAULT_SUPER_USER_FOR_ADMIN.to_owned(),
332 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
333 )
334 .await
335 .unwrap();
336
337 let admin_user_after = user_info_reader
338 .read_guard()
339 .get_user_by_name("admin_user")
340 .cloned()
341 .unwrap();
342
343 assert!(admin_user_after.is_admin);
344 assert!(admin_user_after.is_super);
345
346 frontend
348 .run_user_sql(
349 "ALTER USER admin_user WITH NOADMIN NOSUPERUSER",
350 DEFAULT_DATABASE_NAME.to_owned(),
351 DEFAULT_SUPER_USER_FOR_ADMIN.to_owned(),
352 DEFAULT_SUPER_USER_FOR_ADMIN_ID,
353 )
354 .await
355 .unwrap();
356
357 let admin_user_after = user_info_reader
358 .read_guard()
359 .get_user_by_name("admin_user")
360 .cloned()
361 .unwrap();
362
363 assert!(!admin_user_after.is_admin);
364 assert!(!admin_user_after.is_super);
365 }
366}