forked from StarRocks/starrocks
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Refactor] Separate the Author and Authentication Analyzers (StarRock…
…s#49234) Signed-off-by: HangyuanLiu <[email protected]>
- Loading branch information
1 parent
6eea7aa
commit 2be5b01
Showing
6 changed files
with
233 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
207 changes: 207 additions & 0 deletions
207
fe/fe-core/src/main/java/com/starrocks/sql/analyzer/AuthenticationAnalyzer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
// Copyright 2021-present StarRocks, Inc. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
package com.starrocks.sql.analyzer; | ||
|
||
import com.google.common.base.Strings; | ||
import com.starrocks.authentication.AuthenticationException; | ||
import com.starrocks.authentication.AuthenticationMgr; | ||
import com.starrocks.authentication.AuthenticationProvider; | ||
import com.starrocks.authentication.AuthenticationProviderFactory; | ||
import com.starrocks.authentication.PlainPasswordAuthenticationProvider; | ||
import com.starrocks.authentication.UserAuthenticationInfo; | ||
import com.starrocks.common.Config; | ||
import com.starrocks.mysql.MysqlPassword; | ||
import com.starrocks.privilege.AuthorizationMgr; | ||
import com.starrocks.qe.ConnectContext; | ||
import com.starrocks.server.GlobalStateMgr; | ||
import com.starrocks.sql.ast.AlterUserStmt; | ||
import com.starrocks.sql.ast.AstVisitor; | ||
import com.starrocks.sql.ast.CreateUserStmt; | ||
import com.starrocks.sql.ast.DropUserStmt; | ||
import com.starrocks.sql.ast.ExecuteAsStmt; | ||
import com.starrocks.sql.ast.ShowAuthenticationStmt; | ||
import com.starrocks.sql.ast.StatementBase; | ||
import com.starrocks.sql.ast.UserAuthOption; | ||
import com.starrocks.sql.ast.UserIdentity; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
|
||
public class AuthenticationAnalyzer { | ||
public static void analyze(StatementBase statement, ConnectContext session) { | ||
new AuthenticationAnalyzerVisitor().analyze(statement, session); | ||
} | ||
|
||
public static class AuthenticationAnalyzerVisitor implements AstVisitor<Void, ConnectContext> { | ||
private AuthenticationMgr authenticationManager = null; | ||
private AuthorizationMgr authorizationManager = null; | ||
|
||
public void analyze(StatementBase statement, ConnectContext session) { | ||
authenticationManager = GlobalStateMgr.getCurrentState().getAuthenticationMgr(); | ||
authorizationManager = GlobalStateMgr.getCurrentState().getAuthorizationMgr(); | ||
visit(statement, session); | ||
} | ||
|
||
/** | ||
* analyse user identity + check if user exists in UserPrivTable | ||
*/ | ||
private void analyseUser(UserIdentity userIdent, boolean checkExist) { | ||
userIdent.analyze(); | ||
|
||
// check if user exists | ||
if (checkExist && !authenticationManager.doesUserExist(userIdent)) { | ||
throw new SemanticException("cannot find user " + userIdent + "!"); | ||
} | ||
} | ||
|
||
/** | ||
* check if role name valid and get full role name | ||
*/ | ||
private void validRoleName(String roleName, String errMsg, boolean checkExist) { | ||
// always set to true, we can validate if it's allowed to operation on admin later | ||
FeNameFormat.checkRoleName(roleName, true, errMsg); | ||
// check if role exists | ||
if (checkExist && !authorizationManager.checkRoleExists(roleName)) { | ||
throw new SemanticException(errMsg + ": cannot find role " + roleName + "!"); | ||
} | ||
} | ||
|
||
/** | ||
* Get scrambled password from plain password | ||
*/ | ||
private byte[] analysePassword(String originalPassword, boolean isPasswordPlain) { | ||
if (Strings.isNullOrEmpty(originalPassword)) { | ||
return MysqlPassword.EMPTY_PASSWORD; | ||
} | ||
if (isPasswordPlain) { | ||
return MysqlPassword.makeScrambledPassword(originalPassword); | ||
} else { | ||
return MysqlPassword.checkPassword(originalPassword); | ||
} | ||
} | ||
|
||
@Override | ||
public Void visitCreateUserStatement(CreateUserStmt stmt, ConnectContext context) { | ||
stmt.getUserIdentity().analyze(); | ||
if (authenticationManager.doesUserExist(stmt.getUserIdentity()) && !stmt.isIfNotExists()) { | ||
throw new SemanticException("Operation CREATE USER failed for " + stmt.getUserIdentity() | ||
+ " : user already exists"); | ||
} | ||
if (!stmt.getDefaultRoles().isEmpty()) { | ||
stmt.getDefaultRoles().forEach(r -> validRoleName(r, "Valid role name fail", true)); | ||
} | ||
|
||
UserAuthenticationInfo userAuthenticationInfo = analyzeAuthOption(stmt.getUserIdentity(), stmt.getAuthOption()); | ||
stmt.setAuthenticationInfo(userAuthenticationInfo); | ||
return null; | ||
} | ||
|
||
@Override | ||
public Void visitAlterUserStatement(AlterUserStmt stmt, ConnectContext context) { | ||
stmt.getUserIdentity().analyze(); | ||
if (!authenticationManager.doesUserExist(stmt.getUserIdentity()) && !stmt.isIfExists()) { | ||
throw new SemanticException("Operation ALTER USER failed for " + stmt.getUserIdentity() | ||
+ " : user not exists"); | ||
} | ||
|
||
UserAuthenticationInfo userAuthenticationInfo = analyzeAuthOption(stmt.getUserIdentity(), stmt.getAuthOption()); | ||
stmt.setAuthenticationInfo(userAuthenticationInfo); | ||
|
||
if (!Config.enable_password_reuse) { | ||
UserIdentity user = stmt.getUserIdentity(); | ||
GlobalStateMgr.getCurrentState().getAuthenticationMgr().checkPlainPassword( | ||
user.getUser(), user.getHost(), stmt.getAuthOption().getPassword()); | ||
} | ||
return null; | ||
} | ||
|
||
private UserAuthenticationInfo analyzeAuthOption(UserIdentity userIdentity, UserAuthOption userAuthOption) { | ||
byte[] password = MysqlPassword.EMPTY_PASSWORD; | ||
String authPluginUsing; | ||
if (userAuthOption == null) { | ||
authPluginUsing = authenticationManager.getDefaultPlugin(); | ||
} else { | ||
authPluginUsing = userAuthOption.getAuthPlugin(); | ||
if (authPluginUsing == null) { | ||
authPluginUsing = authenticationManager.getDefaultPlugin(); | ||
password = analysePassword(userAuthOption.getPassword(), userAuthOption.isPasswordPlain()); | ||
} else { | ||
authPluginUsing = userAuthOption.getAuthPlugin(); | ||
if (authPluginUsing.equals(PlainPasswordAuthenticationProvider.PLUGIN_NAME)) { | ||
// In this case, authString is the password | ||
password = analysePassword(userAuthOption.getAuthString(), userAuthOption.isPasswordPlain()); | ||
} | ||
} | ||
} | ||
|
||
try { | ||
AuthenticationProvider provider = AuthenticationProviderFactory.create(authPluginUsing); | ||
UserAuthenticationInfo info = provider.validAuthenticationInfo( | ||
userIdentity, new String(password, StandardCharsets.UTF_8), | ||
userAuthOption == null ? null : userAuthOption.getAuthString()); | ||
info.setAuthPlugin(authPluginUsing); | ||
info.setOrigUserHost(userIdentity.getUser(), userIdentity.getHost()); | ||
return info; | ||
} catch (AuthenticationException e) { | ||
throw new SemanticException("invalidate authentication: " + e.getMessage(), e); | ||
} | ||
} | ||
|
||
private boolean needProtectAdminUser(UserIdentity userIdentity, ConnectContext context) { | ||
return Config.authorization_enable_admin_user_protection && | ||
userIdentity.getUser().equalsIgnoreCase("admin") && | ||
!context.getCurrentUserIdentity().equals(UserIdentity.ROOT); | ||
} | ||
|
||
@Override | ||
public Void visitDropUserStatement(DropUserStmt stmt, ConnectContext session) { | ||
UserIdentity userIdentity = stmt.getUserIdentity(); | ||
userIdentity.analyze(); | ||
|
||
if (needProtectAdminUser(userIdentity, session)) { | ||
throw new SemanticException("'admin' user cannot be dropped because of " + | ||
"'authorization_enable_admin_user_protection' configuration is enabled"); | ||
} | ||
|
||
if (!authenticationManager.doesUserExist(userIdentity) && !stmt.isIfExists()) { | ||
throw new SemanticException("Operation DROP USER failed for " + userIdentity + " : user not exists"); | ||
} | ||
|
||
if (stmt.getUserIdentity().equals(UserIdentity.ROOT)) { | ||
throw new SemanticException("Operation DROP USER failed for " + UserIdentity.ROOT + | ||
" : cannot drop user " + UserIdentity.ROOT); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Void visitShowAuthenticationStatement(ShowAuthenticationStmt statement, ConnectContext context) { | ||
UserIdentity user = statement.getUserIdent(); | ||
if (user != null) { | ||
analyseUser(user, true); | ||
} else if (!statement.isAll()) { | ||
statement.setUserIdent(context.getCurrentUserIdentity()); | ||
} | ||
return null; | ||
} | ||
|
||
@Override | ||
public Void visitExecuteAsStatement(ExecuteAsStmt stmt, ConnectContext session) { | ||
if (stmt.isAllowRevert()) { | ||
throw new SemanticException("`EXECUTE AS` must use with `WITH NO REVERT` for now!"); | ||
} | ||
analyseUser(stmt.getToUser(), true); | ||
return null; | ||
} | ||
} | ||
} |
Oops, something went wrong.