diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf
index a5113ba28ee7fcbe61a3e6b75f2fccf29332a3ec..dc35cccaf2a5667686d2b582becfdbb430ddab8d 100644
--- a/doc/sql/query.ebnf
+++ b/doc/sql/query.ebnf
@@ -3,10 +3,11 @@ EXPLAIN     ::= 'EXPLAIN' ( DML | DQL )
 DQL         ::= (SELECT | SELECT UNION ALL SELECT | SELECT (EXCEPT 'DISTINCT'? ) SELECT ) Options?
 DML         ::= (DELETE | INSERT | UPDATE) Options?
 DDL         ::= CreateTable | DropTable
-ACL         ::= DropRole | DropUser | CreateRole | CreateUser
+ACL         ::= DropRole | DropUser | CreateRole | CreateUser | AlterUser
 CreateRole  ::= 'create role' role ('option' '(' ('timeout' '=' DOUBLE)')')?
 CreateTable ::= 'create table' table '(' Column (',' Column)* ',' PrimaryKey ')' ('using' ('memtx' | 'vinyl'))? Distribution ('option' '(' ('timeout' '=' DOUBLE)')')?
-CreateUser ::= 'create user' user 'with'? 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))? ('option' '(' ('timeout' '=' DOUBLE)')')?
+CreateUser  ::= 'create user' user 'with'? 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))? ('option' '(' ('timeout' '=' DOUBLE)')')?
+AlterUser   ::= 'alter user' user 'with'? 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))? ('option' '(' ('timeout' '=' DOUBLE)')')?
 Column      ::= name ('Bool' | 'Decimal' | 'Double' | 'Int' | 'Number' | 'Scalar' | 'String' | 'Text' | 'Unsigned' | 'Varchar') (('not'?)  'null')?
 PrimaryKey  ::= 'primary key' '(' name (',' name)* ')'
 Distribution ::= 'global' | ('distributed by' '(' name (',' name)*  ')')
diff --git a/sbroad-cartridge/test_app/test/integration/acl_test.lua b/sbroad-cartridge/test_app/test/integration/acl_test.lua
index 55341f31cdbd83d84a6ad68a02797a45763c537d..99f987eb23c53bdb82cea543b9c8a093f95c3cbc 100644
--- a/sbroad-cartridge/test_app/test/integration/acl_test.lua
+++ b/sbroad-cartridge/test_app/test/integration/acl_test.lua
@@ -68,3 +68,16 @@ g.test_create_user = function()
             [[Sbroad Error: ACL queries are not supported]]
     )
 end
+
+g.test_alter_user = function()
+    local api = cluster:server("api-1").net_box
+
+    local _, err = api:call(
+            "sbroad.execute",
+            { [[ ALTER USER "user" WITH PASSWORD '123' USING MD5 ]], {} }
+    )
+    t.assert_equals(
+            string.format("%s", err),
+            [[Sbroad Error: ACL queries are not supported]]
+    )
+end
diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index a52babb146a3a8c1fcc4722033ee8ba0a4a4013d..26a0d6d291860cb2cced0484416e728ee97b40a6 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -427,6 +427,76 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl,
     Ok(create_sharded_table)
 }
 
+/// Code block common for CREATE/ALTER USER moved to separate function.
+fn parse_create_alter_user(
+    ast: &AbstractSyntaxTree,
+    node: &ParseNode,
+    default_timeout: Decimal,
+) -> Result<(String, String, String, Decimal), SbroadError> {
+    let mut iter = node.children.iter();
+    // User name
+    let user_name_id = iter.next().ok_or_else(|| {
+        SbroadError::Invalid(
+            Entity::ParseNode,
+            Some(String::from("UserName expected as a first child")),
+        )
+    })?;
+    let user_name_node = ast.nodes.get_node(*user_name_id)?;
+    let user_name =
+        normalize_name_for_space_api(user_name_node.value.as_ref().ok_or_else(|| {
+            SbroadError::NotFound(Entity::Node, "user name in UserName node".into())
+        })?);
+    // Password
+    let pwd_id = iter.next().ok_or_else(|| {
+        SbroadError::Invalid(
+            Entity::ParseNode,
+            Some(String::from("Password expected as a second child")),
+        )
+    })?;
+    let pwd_node = ast.nodes.get_node(*pwd_id)?;
+    let password: String = pwd_node
+        .value
+        .as_ref()
+        .ok_or_else(|| SbroadError::NotFound(Entity::Node, "password in Password node".into()))?
+        .to_string();
+    // Optional parameters: auth method and timeout.
+    let mut timeout = default_timeout;
+    let mut auth_method = String::from("chap-sha1");
+    for child_id in iter {
+        let child_node = ast.nodes.get_node(*child_id)?;
+        match child_node.rule {
+            Type::Timeout => {
+                timeout = get_timeout(ast, *child_id)?;
+            }
+            Type::AuthMethod => {
+                let method_id = child_node.children.first().ok_or_else(|| {
+                    SbroadError::Invalid(
+                        Entity::ParseNode,
+                        Some(String::from("Method expected under AuthMethod node")),
+                    )
+                })?;
+                let method_node = ast.nodes.get_node(*method_id)?;
+                auth_method = method_node
+                    .value
+                    .as_ref()
+                    .ok_or_else(|| {
+                        SbroadError::NotFound(Entity::Node, "auth method in AuthMethod node".into())
+                    })?
+                    .to_string();
+            }
+            _ => {
+                return Err(SbroadError::Invalid(
+                    Entity::Node,
+                    Some(format!(
+                        "ACL node contains unexpected child: {child_node:?}",
+                    )),
+                ));
+            }
+        }
+    }
+    Ok((user_name, password, auth_method, timeout))
+}
+
 impl Ast for AbstractSyntaxTree {
     /// Build an empty AST.
     fn empty() -> Self {
@@ -2009,81 +2079,21 @@ impl Ast for AbstractSyntaxTree {
                     let plan_id = plan.nodes.push(Node::Ddl(drop_table));
                     map.add(id, plan_id);
                 }
+                Type::AlterUser => {
+                    let (user_name, password, auth_method, timeout) =
+                        parse_create_alter_user(self, node, default_timeout)?;
+                    let alter_user = Acl::AlterUser {
+                        name: user_name,
+                        password,
+                        auth_method,
+                        timeout,
+                    };
+                    let plan_id = plan.nodes.push(Node::Acl(alter_user));
+                    map.add(id, plan_id);
+                }
                 Type::CreateUser => {
-                    let mut iter = node.children.iter();
-                    // User name
-                    let user_name_id = iter.next().ok_or_else(|| {
-                        SbroadError::Invalid(
-                            Entity::ParseNode,
-                            Some(String::from("UserName expected under CreateUser node")),
-                        )
-                    })?;
-                    let user_name_node = self.nodes.get_node(*user_name_id)?;
-                    let user_name = normalize_name_for_space_api(
-                        user_name_node.value.as_ref().ok_or_else(|| {
-                            SbroadError::NotFound(
-                                Entity::Node,
-                                "user name in the create user AST".into(),
-                            )
-                        })?,
-                    );
-                    // Password
-                    let pwd_id = iter.next().ok_or_else(|| {
-                        SbroadError::Invalid(
-                            Entity::ParseNode,
-                            Some(String::from("Password expected under CreateUser node")),
-                        )
-                    })?;
-                    let pwd_node = self.nodes.get_node(*pwd_id)?;
-                    let password: String = pwd_node
-                        .value
-                        .as_ref()
-                        .ok_or_else(|| {
-                            SbroadError::NotFound(
-                                Entity::Node,
-                                "password in the create user AST".into(),
-                            )
-                        })?
-                        .to_string();
-                    // Optional parameters: auth method and timeout.
-                    let mut timeout = default_timeout;
-                    let mut auth_method = String::from("chap-sha1");
-                    for child_id in iter {
-                        let child_node = self.nodes.get_node(*child_id)?;
-                        match child_node.rule {
-                            Type::Timeout => {
-                                timeout = get_timeout(self, *child_id)?;
-                            }
-                            Type::AuthMethod => {
-                                let method_id = child_node.children.first().ok_or_else(|| {
-                                    SbroadError::Invalid(
-                                        Entity::ParseNode,
-                                        Some(String::from("Method expected under AuthMethod node")),
-                                    )
-                                })?;
-                                let method_node = self.nodes.get_node(*method_id)?;
-                                auth_method = method_node
-                                    .value
-                                    .as_ref()
-                                    .ok_or_else(|| {
-                                        SbroadError::NotFound(
-                                            Entity::Node,
-                                            "auth method in the create user AST".into(),
-                                        )
-                                    })?
-                                    .to_string();
-                            }
-                            _ => {
-                                return Err(SbroadError::Invalid(
-                                    Entity::Node,
-                                    Some(format!(
-                                        "AST create user node {:?} contains unexpected children",
-                                        child_node,
-                                    )),
-                                ));
-                            }
-                        }
-                    }
+                    let (user_name, password, auth_method, timeout) =
+                        parse_create_alter_user(self, node, default_timeout)?;
                     let create_user = Acl::CreateUser {
                         name: user_name,
                         password,
diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs
index e5393d3e9ab752d95a965a11f7128c19a3bdeeee..40f58b1774d92fede1a941105671533cb69907cc 100644
--- a/sbroad-core/src/frontend/sql/ast.rs
+++ b/sbroad-core/src/frontend/sql/ast.rs
@@ -29,6 +29,7 @@ pub enum Type {
     Addition,
     Alias,
     AliasName,
+    AlterUser,
     And,
     ArithmeticExpr,
     ArithParentheses,
@@ -161,6 +162,7 @@ impl Type {
             Rule::Addition => Ok(Type::Addition),
             Rule::Alias => Ok(Type::Alias),
             Rule::AliasName => Ok(Type::AliasName),
+            Rule::AlterUser => Ok(Type::AlterUser),
             Rule::And => Ok(Type::And),
             Rule::ArithmeticExpr => Ok(Type::ArithmeticExpr),
             Rule::ArithParentheses => Ok(Type::ArithParentheses),
@@ -298,6 +300,7 @@ impl fmt::Display for Type {
             Type::Addition => "Addition".to_string(),
             Type::Alias => "Alias".to_string(),
             Type::AliasName => "AliasName".to_string(),
+            Type::AlterUser => "AlterUser".to_string(),
             Type::And => "And".to_string(),
             Type::ArithmeticExpr => "ArithmeticExpr".to_string(),
             Type::ArithParentheses => "ArithParentheses".to_string(),
diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest
index 0eb0022ef6031bd0e0c22bf5389d6322ce42b839..1cf5118dde02335030324d09923e8c3a89e0ada5 100644
--- a/sbroad-core/src/frontend/sql/query.pest
+++ b/sbroad-core/src/frontend/sql/query.pest
@@ -1,12 +1,16 @@
 Command = _{ SOI ~ (ExplainQuery | Query | DDL | ACL) ~ EOF }
 
-ACL = _{ DropRole | DropUser | CreateRole | CreateUser }
+ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser }
     CreateUser = {
         ^"create" ~ ^"user" ~ UserName ~ (^"with")? ~ ^"password" ~ PasswordString ~
         AuthMethod? ~ Option?
     }
         PasswordString = _{ "'" ~ Password ~ "'" }
             Password = @{ String }
+    AlterUser = {
+        ^"alter" ~ ^"user" ~ UserName ~ (^"with")? ~ ^"password" ~ PasswordString ~
+        AuthMethod? ~ Option?
+    }
 	AuthMethod = { ^"using" ~ (ChapSha1 | Md5 | Ldap) }
             ChapSha1 = { ^"chap-sha1" }
             Md5 = { ^"md5" }
diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs
index 0d8d3a2a03e659c00ea29e64cb12da8a8572a084..029d512862dc202aba3ee4a62e37f5a80499d322 100644
--- a/sbroad-core/src/ir/acl.rs
+++ b/sbroad-core/src/ir/acl.rs
@@ -22,6 +22,12 @@ pub enum Acl {
         auth_method: String,
         timeout: Decimal,
     },
+    AlterUser {
+        name: String,
+        password: String,
+        auth_method: String,
+        timeout: Decimal,
+    },
 }
 
 impl Acl {
@@ -34,6 +40,7 @@ impl Acl {
             Acl::DropRole { ref timeout, .. }
             | Acl::DropUser { ref timeout, .. }
             | Acl::CreateRole { ref timeout, .. }
+            | Acl::AlterUser { ref timeout, .. }
             | Acl::CreateUser { ref timeout, .. } => timeout,
         }
         .to_string()