risingwave_frontend/handler/
alter_user.rs1use pgwire::pg_response::{PgResponse, StatementType};
16use risingwave_pb::user::UserInfo;
17use risingwave_pb::user::update_user_request::UpdateField;
18use risingwave_sqlparser::ast::{AlterUserStatement, ObjectName, UserOption, UserOptions};
19
20use super::RwPgResponse;
21use crate::binder::Binder;
22use crate::catalog::CatalogError;
23use crate::error::ErrorCode::{self, InternalError, PermissionDenied};
24use crate::error::Result;
25use crate::handler::HandlerArgs;
26use crate::user::user_authentication::{
27 OAUTH_ISSUER_KEY, OAUTH_JWKS_URL_KEY, build_oauth_info, encrypted_password,
28};
29use crate::user::user_catalog::UserCatalog;
30
31fn alter_prost_user_info(
32 mut user_info: UserInfo,
33 options: &UserOptions,
34 session_user: &UserCatalog,
35) -> Result<(UserInfo, Vec<UpdateField>)> {
36 if !session_user.is_super {
37 let require_super = user_info.is_super
38 || options
39 .0
40 .iter()
41 .any(|option| matches!(option, UserOption::SuperUser | UserOption::NoSuperUser));
42 if require_super {
43 return Err(PermissionDenied(
44 "must be superuser to alter superuser roles or change superuser attribute"
45 .to_owned(),
46 )
47 .into());
48 }
49
50 let change_self_password = session_user.id == user_info.id
51 && options.0.len() == 1
52 && matches!(
53 &options.0[0],
54 UserOption::EncryptedPassword(_) | UserOption::Password(_)
55 );
56 if !session_user.can_create_user && !change_self_password {
57 return Err(PermissionDenied("Do not have the privilege".to_owned()).into());
58 }
59 }
60
61 let mut update_fields = Vec::new();
62 for option in &options.0 {
63 match option {
64 UserOption::SuperUser => {
65 user_info.is_super = true;
66 update_fields.push(UpdateField::Super);
67 }
68 UserOption::NoSuperUser => {
69 user_info.is_super = false;
70 update_fields.push(UpdateField::Super);
71 }
72 UserOption::CreateDB => {
73 user_info.can_create_db = true;
74 update_fields.push(UpdateField::CreateDb);
75 }
76 UserOption::NoCreateDB => {
77 user_info.can_create_db = false;
78 update_fields.push(UpdateField::CreateDb);
79 }
80 UserOption::CreateUser => {
81 user_info.can_create_user = true;
82 update_fields.push(UpdateField::CreateUser);
83 }
84 UserOption::NoCreateUser => {
85 user_info.can_create_user = false;
86 update_fields.push(UpdateField::CreateUser);
87 }
88 UserOption::Login => {
89 user_info.can_login = true;
90 update_fields.push(UpdateField::Login);
91 }
92 UserOption::NoLogin => {
93 user_info.can_login = false;
94 update_fields.push(UpdateField::Login);
95 }
96 UserOption::EncryptedPassword(p) => {
97 if !p.0.is_empty() {
99 user_info.auth_info = encrypted_password(&user_info.name, &p.0);
100 } else {
101 user_info.auth_info = None;
102 };
103 update_fields.push(UpdateField::AuthInfo);
104 }
105 UserOption::Password(opt) => {
106 if let Some(password) = opt
108 && !password.0.is_empty()
109 {
110 user_info.auth_info = encrypted_password(&user_info.name, &password.0);
111 } else {
112 user_info.auth_info = None;
113 }
114 update_fields.push(UpdateField::AuthInfo);
115 }
116 UserOption::OAuth(options) => {
117 let auth_info = build_oauth_info(options).ok_or_else(|| {
118 ErrorCode::InvalidParameterValue(format!(
119 "{} and {} must be provided",
120 OAUTH_JWKS_URL_KEY, OAUTH_ISSUER_KEY
121 ))
122 })?;
123 user_info.auth_info = Some(auth_info);
124 update_fields.push(UpdateField::AuthInfo)
125 }
126 }
127 }
128 Ok((user_info, update_fields))
129}
130
131fn alter_rename_prost_user_info(
132 mut user_info: UserInfo,
133 new_name: ObjectName,
134 session_user: &UserCatalog,
135) -> Result<(UserInfo, Vec<UpdateField>)> {
136 if session_user.id == user_info.id {
137 return Err(InternalError("session user cannot be renamed".to_owned()).into());
138 }
139
140 if !session_user.is_super {
141 if user_info.is_super {
142 return Err(
143 PermissionDenied("must be superuser to rename superusers".to_owned()).into(),
144 );
145 }
146
147 if !session_user.can_create_user {
148 return Err(
149 PermissionDenied("Do not have the privilege to rename user".to_owned()).into(),
150 );
151 }
152 }
153
154 user_info.name = Binder::resolve_user_name(new_name)?;
155 user_info.auth_info = None;
156 Ok((user_info, vec![UpdateField::Rename, UpdateField::AuthInfo]))
157}
158
159pub async fn handle_alter_user(
160 handler_args: HandlerArgs,
161 stmt: AlterUserStatement,
162) -> Result<RwPgResponse> {
163 let session = handler_args.session;
164 let (user_info, update_fields) = {
165 let user_name = Binder::resolve_user_name(stmt.user_name.clone())?;
166 let user_reader = session.env().user_info_reader().read_guard();
167
168 let old_info = user_reader
169 .get_user_by_name(&user_name)
170 .ok_or(CatalogError::NotFound("user", user_name))?
171 .to_prost();
172
173 let session_user = user_reader
174 .get_user_by_name(&session.user_name())
175 .ok_or_else(|| CatalogError::NotFound("user", session.user_name().to_owned()))?;
176
177 match stmt.mode {
178 risingwave_sqlparser::ast::AlterUserMode::Options(options) => {
179 alter_prost_user_info(old_info, &options, session_user)?
180 }
181 risingwave_sqlparser::ast::AlterUserMode::Rename(new_name) => {
182 alter_rename_prost_user_info(old_info, new_name, session_user)?
183 }
184 }
185 };
186
187 let user_info_writer = session.user_info_writer()?;
188 user_info_writer
189 .update_user(user_info, update_fields)
190 .await?;
191 Ok(PgResponse::empty_result(StatementType::UPDATE_USER))
192}
193
194#[cfg(test)]
195mod tests {
196 use std::collections::HashMap;
197
198 use risingwave_pb::user::AuthInfo;
199 use risingwave_pb::user::auth_info::EncryptionType;
200
201 use crate::test_utils::LocalFrontend;
202
203 #[tokio::test]
204 async fn test_alter_user() {
205 let frontend = LocalFrontend::new(Default::default()).await;
206 let session = frontend.session_ref();
207 let user_info_reader = session.env().user_info_reader();
208
209 frontend.run_sql("CREATE USER userB WITH SUPERUSER NOCREATEDB PASSWORD 'md5827ccb0eea8a706c4c34a16891f84e7b'").await.unwrap();
210 frontend
211 .run_sql("ALTER USER userB RENAME TO user")
212 .await
213 .unwrap();
214 assert!(
215 user_info_reader
216 .read_guard()
217 .get_user_by_name("userB")
218 .is_none()
219 );
220 assert!(
221 user_info_reader
222 .read_guard()
223 .get_user_by_name("user")
224 .is_some()
225 );
226
227 frontend.run_sql("ALTER USER user WITH NOSUPERUSER CREATEDB PASSWORD 'md59f2fa6a30871a92249bdd2f1eeee4ef6'").await.unwrap();
228
229 let user_info = user_info_reader
230 .read_guard()
231 .get_user_by_name("user")
232 .cloned()
233 .unwrap();
234 assert!(!user_info.is_super);
235 assert!(user_info.can_create_db);
236 assert_eq!(
237 user_info.auth_info,
238 Some(AuthInfo {
239 encryption_type: EncryptionType::Md5 as i32,
240 encrypted_value: b"9f2fa6a30871a92249bdd2f1eeee4ef6".to_vec(),
241 metadata: HashMap::new(),
242 })
243 );
244 }
245}