Co-authored-by: Harbor Liu <460660596@qq.com>
This commit is contained in:
parent
94342fc8e6
commit
2484b49178
|
|
@ -98,7 +98,7 @@ public class AuthenticationHandler {
|
|||
provider.authenticate(context, matchedUserIdentity.getKey(), authResponse);
|
||||
}
|
||||
|
||||
return new AuthenticationResult(matchedUserIdentity.getKey(), List.of(Config.group_provider), null);
|
||||
return new AuthenticationResult(matchedUserIdentity.getKey(), List.of(Config.group_provider), null, "native");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +138,8 @@ public class AuthenticationHandler {
|
|||
UserIdentity.createEphemeralUserIdent(user, remoteHost),
|
||||
securityIntegration.getGroupProviderName() == null ?
|
||||
List.of(Config.group_provider) : securityIntegration.getGroupProviderName(),
|
||||
securityIntegration.getGroupAllowedLoginList());
|
||||
securityIntegration.getGroupAllowedLoginList(),
|
||||
authMechanism);
|
||||
}
|
||||
|
||||
if (authenticationResult == null && !exceptions.isEmpty()) {
|
||||
|
|
@ -154,19 +155,38 @@ public class AuthenticationHandler {
|
|||
throws AuthenticationException {
|
||||
String user = authenticationResult.authenticatedUser.getUser();
|
||||
|
||||
// Step 1: Set user identity to context
|
||||
// Set the authenticated user identity as the current user for authorization purposes
|
||||
context.setCurrentUserIdentity(authenticationResult.authenticatedUser);
|
||||
if (!authenticationResult.authenticatedUser.isEphemeral()) {
|
||||
context.setCurrentRoleIds(authenticationResult.authenticatedUser);
|
||||
|
||||
UserProperty userProperty = GlobalStateMgr.getCurrentState().getAuthenticationMgr()
|
||||
.getUserProperty(authenticationResult.authenticatedUser.getUser());
|
||||
context.updateByUserProperty(userProperty);
|
||||
}
|
||||
// Set the qualified username for this connection session
|
||||
context.setQualifiedUser(user);
|
||||
|
||||
Set<String> groups = getGroups(authenticationResult.authenticatedUser, authenticationResult.groupProviderName);
|
||||
context.setGroups(groups);
|
||||
// Step 2: Set distinguished name to context if it is empty
|
||||
// Distinguished name is used for LDAP authentication and group resolution
|
||||
// If not already set, use the username as the distinguished name
|
||||
if (context.getDistinguishedName().isEmpty()) {
|
||||
context.setDistinguishedName(user);
|
||||
}
|
||||
|
||||
// Step 3: Set security integration to context
|
||||
// Record which security integration method was used for authentication
|
||||
// This helps track authentication method (native, LDAP, OAuth2, etc.)
|
||||
if (authenticationResult.securityIntegration != null) {
|
||||
context.setSecurityIntegration(authenticationResult.securityIntegration);
|
||||
}
|
||||
|
||||
// Step 4: Resolve and set user groups
|
||||
// Get user groups from configured group providers (e.g., LDAP groups)
|
||||
// Groups are used for role-based access control and permission management
|
||||
Set<String> groups = getGroups(context.getCurrentUserIdentity(), context.getDistinguishedName(),
|
||||
authenticationResult.groupProviderName);
|
||||
context.setGroups(groups);
|
||||
// Set current role IDs based on the authenticated user and groups
|
||||
context.setCurrentRoleIds(authenticationResult.authenticatedUser, groups);
|
||||
|
||||
// Step 5: Validate group access permissions
|
||||
// If authentication result specifies allowed groups, verify user belongs to at least one
|
||||
// This ensures users can only access groups they are authorized for
|
||||
if (authenticationResult.authenticatedGroupList != null && !authenticationResult.authenticatedGroupList.isEmpty()) {
|
||||
Set<String> intersection = new HashSet<>(groups);
|
||||
intersection.retainAll(authenticationResult.authenticatedGroupList);
|
||||
|
|
@ -174,23 +194,35 @@ public class AuthenticationHandler {
|
|||
throw new AuthenticationException(ErrorCode.ERR_GROUP_ACCESS_DENY, user, Joiner.on(",").join(groups));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthenticationResult {
|
||||
private UserIdentity authenticatedUser = null;
|
||||
private List<String> groupProviderName = null;
|
||||
private List<String> authenticatedGroupList = null;
|
||||
|
||||
public AuthenticationResult(UserIdentity authenticatedUser,
|
||||
List<String> groupProviderName,
|
||||
List<String> authenticatedGroupList) {
|
||||
this.authenticatedUser = authenticatedUser;
|
||||
this.groupProviderName = groupProviderName;
|
||||
this.authenticatedGroupList = authenticatedGroupList;
|
||||
// Step 6: Apply user properties for non-ephemeral users
|
||||
// Load and apply user-specific properties (session variables, resource limits, etc.)
|
||||
// Ephemeral users (from external auth) don't have stored properties
|
||||
if (!authenticationResult.authenticatedUser.isEphemeral()) {
|
||||
UserProperty userProperty = GlobalStateMgr.getCurrentState().getAuthenticationMgr()
|
||||
.getUserProperty(authenticationResult.authenticatedUser.getUser());
|
||||
context.updateByUserProperty(userProperty);
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> getGroups(UserIdentity userIdentity, List<String> groupProviderList) {
|
||||
private static class AuthenticationResult {
|
||||
private final UserIdentity authenticatedUser;
|
||||
private final List<String> groupProviderName;
|
||||
private final List<String> authenticatedGroupList;
|
||||
private final String securityIntegration;
|
||||
|
||||
public AuthenticationResult(UserIdentity authenticatedUser,
|
||||
List<String> groupProviderName,
|
||||
List<String> authenticatedGroupList,
|
||||
String securityIntegration) {
|
||||
this.authenticatedUser = authenticatedUser;
|
||||
this.groupProviderName = groupProviderName;
|
||||
this.authenticatedGroupList = authenticatedGroupList;
|
||||
this.securityIntegration = securityIntegration;
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<String> getGroups(UserIdentity userIdentity, String distinguishedName, List<String> groupProviderList) {
|
||||
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
|
||||
|
||||
HashSet<String> groups = new HashSet<>();
|
||||
|
|
@ -199,7 +231,7 @@ public class AuthenticationHandler {
|
|||
if (groupProvider == null) {
|
||||
continue;
|
||||
}
|
||||
groups.addAll(groupProvider.getGroup(userIdentity));
|
||||
groups.addAll(groupProvider.getGroup(userIdentity, distinguishedName));
|
||||
}
|
||||
|
||||
return groups;
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ public class AuthenticationMgr {
|
|||
|
||||
public void dropGroupProviderStatement(DropGroupProviderStmt stmt, ConnectContext context) {
|
||||
GroupProvider groupProvider = this.nameToGroupProviderMap.remove(stmt.getName());
|
||||
groupProvider.destory();
|
||||
groupProvider.destroy();
|
||||
|
||||
GlobalStateMgr.getCurrentState().getEditLog().logEdit(OperationType.OP_DROP_GROUP_PROVIDER,
|
||||
new GroupProviderLog(stmt.getName(), null));
|
||||
|
|
@ -651,7 +651,7 @@ public class AuthenticationMgr {
|
|||
|
||||
public void replayDropGroupProvider(String name) {
|
||||
GroupProvider groupProvider = this.nameToGroupProviderMap.remove(name);
|
||||
groupProvider.destory();
|
||||
groupProvider.destroy();
|
||||
}
|
||||
|
||||
public List<GroupProvider> getAllGroupProviders() {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,9 @@
|
|||
package com.starrocks.authentication;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.starrocks.StarRocksFE;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.sql.analyzer.SemanticException;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
|
@ -37,8 +34,6 @@ import java.util.Set;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class FileGroupProvider extends GroupProvider {
|
||||
private static final Logger LOG = LogManager.getLogger(FileGroupProvider.class);
|
||||
|
||||
public static final String TYPE = "file";
|
||||
public static final String GROUP_FILE_URL = "group_file_url";
|
||||
public static final Set<String> REQUIRED_PROPERTIES = new HashSet<>(List.of(FileGroupProvider.GROUP_FILE_URL));
|
||||
|
|
@ -79,7 +74,7 @@ public class FileGroupProvider extends GroupProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroup(UserIdentity userIdentity) {
|
||||
public Set<String> getGroup(UserIdentity userIdentity, String distinguishedName) {
|
||||
return userGroups.getOrDefault(userIdentity.getUser(), new HashSet<>());
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +92,14 @@ public class FileGroupProvider extends GroupProvider {
|
|||
if (groupFileUrl.startsWith("http://") || groupFileUrl.startsWith("https://")) {
|
||||
return new URL(groupFileUrl).openStream();
|
||||
} else {
|
||||
String filePath = StarRocksFE.STARROCKS_HOME_DIR + "/conf/" + groupFileUrl;
|
||||
String starRocksHome = System.getenv("STARROCKS_HOME");
|
||||
String filePath;
|
||||
if (starRocksHome != null) {
|
||||
filePath = starRocksHome + "/conf/" + groupFileUrl;
|
||||
} else {
|
||||
// If STARROCKS_HOME is not set, use absolute path
|
||||
filePath = groupFileUrl;
|
||||
}
|
||||
return new FileInputStream(filePath);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public abstract class GroupProvider {
|
|||
|
||||
}
|
||||
|
||||
public void destory() {
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ public abstract class GroupProvider {
|
|||
return "";
|
||||
}
|
||||
|
||||
public abstract Set<String> getGroup(UserIdentity userIdentity);
|
||||
public abstract Set<String> getGroup(UserIdentity userIdentity, String distinguishedName);
|
||||
|
||||
public abstract void checkProperty() throws SemanticException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package com.starrocks.authentication;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.starrocks.common.util.NetUtils;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
|
|
@ -80,11 +81,17 @@ public class LDAPAuthProvider implements AuthenticationProvider {
|
|||
}
|
||||
|
||||
try {
|
||||
String distinguishedName;
|
||||
if (!Strings.isNullOrEmpty(ldapUserDN)) {
|
||||
checkPassword(ldapUserDN, new String(clearPassword, StandardCharsets.UTF_8));
|
||||
distinguishedName = ldapUserDN;
|
||||
} else {
|
||||
checkPasswordByRoot(userIdentity.getUser(), new String(clearPassword, StandardCharsets.UTF_8));
|
||||
distinguishedName = findUserDNByRoot(userIdentity.getUser());
|
||||
}
|
||||
Preconditions.checkNotNull(distinguishedName);
|
||||
checkPassword(distinguishedName, new String(clearPassword, StandardCharsets.UTF_8));
|
||||
|
||||
// set distinguished name to auth context
|
||||
context.setDistinguishedName(distinguishedName);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("check password failed for user: {}", userIdentity.getUser(), e);
|
||||
throw new AuthenticationException(e.getMessage());
|
||||
|
|
@ -111,7 +118,7 @@ public class LDAPAuthProvider implements AuthenticationProvider {
|
|||
}
|
||||
|
||||
//bind to ldap server to check password
|
||||
public void checkPassword(String dn, String password) throws Exception {
|
||||
protected void checkPassword(String dn, String password) throws Exception {
|
||||
if (Strings.isNullOrEmpty(password)) {
|
||||
throw new AuthenticationException("empty password is not allowed for simple authentication");
|
||||
}
|
||||
|
|
@ -143,8 +150,8 @@ public class LDAPAuthProvider implements AuthenticationProvider {
|
|||
|
||||
//1. bind ldap server by root dn
|
||||
//2. search user
|
||||
//3. if match exactly one, check password
|
||||
public void checkPasswordByRoot(String user, String password) throws Exception {
|
||||
//3. if match exactly one, return the user's actual DN
|
||||
protected String findUserDNByRoot(String user) throws Exception {
|
||||
if (Strings.isNullOrEmpty(ldapBindRootPwd)) {
|
||||
throw new AuthenticationException("empty password is not allowed for simple authentication");
|
||||
}
|
||||
|
|
@ -198,7 +205,7 @@ public class LDAPAuthProvider implements AuthenticationProvider {
|
|||
throw new AuthenticationException("ldap search matched user count " + matched);
|
||||
}
|
||||
|
||||
checkPassword(userDN, password);
|
||||
return userDN;
|
||||
} finally {
|
||||
if (ctx != null) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
package com.starrocks.authentication;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
|
|
@ -128,16 +129,22 @@ public class LDAPGroupProvider extends GroupProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void destory() {
|
||||
public void destroy() {
|
||||
scheduleTask.cancel(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroup(UserIdentity userIdentity) {
|
||||
return userToGroupCache.getOrDefault(userIdentity.getUser(), Set.of());
|
||||
public Set<String> getGroup(UserIdentity userIdentity, String distinguishedName) {
|
||||
String ldapUserSearchAttr = getLdapUserSearchAttr();
|
||||
if (ldapUserSearchAttr != null) {
|
||||
return userToGroupCache.getOrDefault(userIdentity.getUser(), Set.of());
|
||||
} else {
|
||||
return userToGroupCache.getOrDefault(distinguishedName, Set.of());
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshGroups() {
|
||||
LOG.info("refresh ldap group cache for group provider: {}", name);
|
||||
Map<String, Set<String>> groups = new ConcurrentHashMap<>();
|
||||
try {
|
||||
DirContext ctx = createDirContextOnConnection(getLdapBindRootDn(), getLdapBindRootPwd());
|
||||
|
|
@ -397,4 +404,9 @@ public class LDAPGroupProvider extends GroupProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setUserToGroupCache(Map<String, Set<String>> userToGroupCache) {
|
||||
this.userToGroupCache = userToGroupCache;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public class UnixGroupProvider extends GroupProvider {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getGroup(UserIdentity userIdentity) {
|
||||
public Set<String> getGroup(UserIdentity userIdentity, String distinguishedName) {
|
||||
Set<String> userGroups = Set.of();
|
||||
|
||||
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(userIdentity.getUser());
|
||||
|
|
|
|||
|
|
@ -129,6 +129,14 @@ public class ErrorReport {
|
|||
report(null, errorCode, objs);
|
||||
}
|
||||
|
||||
public static void report(ErrorCode errorCode, String errMsg) {
|
||||
ConnectContext ctx = ConnectContext.get();
|
||||
if (ctx != null) {
|
||||
ctx.getState().setError(errMsg);
|
||||
ctx.getState().setErrorCode(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void report(String pattern, ErrorCode errorCode, Object... objs) {
|
||||
reportCommon(pattern, errorCode, objs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,6 +199,15 @@ public class ConnectContext {
|
|||
//Auth Data salt generated at mysql negotiate used for password salting
|
||||
private byte[] authDataSalt = null;
|
||||
|
||||
// The security integration method used for authentication.
|
||||
protected String securityIntegration = "native";
|
||||
|
||||
// Distinguished name (DN) used for LDAP authentication and group resolution
|
||||
// In LDAP context, this represents the unique identifier of a user in the directory
|
||||
// For non-LDAP authentication, this typically defaults to the username
|
||||
// Used by group providers to resolve user group memberships
|
||||
protected String distinguishedName = "";
|
||||
|
||||
// Serializer used to pack MySQL packet.
|
||||
protected MysqlSerializer serializer;
|
||||
// Variables belong to this session.
|
||||
|
|
@ -491,21 +500,33 @@ public class ConnectContext {
|
|||
this.currentUserIdentity = currentUserIdentity;
|
||||
}
|
||||
|
||||
public void setDistinguishedName(String distinguishedName) {
|
||||
this.distinguishedName = distinguishedName;
|
||||
}
|
||||
|
||||
public String getDistinguishedName() {
|
||||
return distinguishedName;
|
||||
}
|
||||
|
||||
public Set<Long> getCurrentRoleIds() {
|
||||
return currentRoleIds;
|
||||
}
|
||||
|
||||
public void setCurrentRoleIds(UserIdentity user) {
|
||||
try {
|
||||
Set<Long> defaultRoleIds;
|
||||
if (GlobalVariable.isActivateAllRolesOnLogin()) {
|
||||
defaultRoleIds = globalStateMgr.getAuthorizationMgr().getRoleIdsByUser(user);
|
||||
} else {
|
||||
defaultRoleIds = globalStateMgr.getAuthorizationMgr().getDefaultRoleIdsByUser(user);
|
||||
if (user.isEphemeral()) {
|
||||
this.currentRoleIds = new HashSet<>();
|
||||
} else {
|
||||
try {
|
||||
Set<Long> defaultRoleIds;
|
||||
if (GlobalVariable.isActivateAllRolesOnLogin()) {
|
||||
defaultRoleIds = globalStateMgr.getAuthorizationMgr().getRoleIdsByUser(user);
|
||||
} else {
|
||||
defaultRoleIds = globalStateMgr.getAuthorizationMgr().getDefaultRoleIdsByUser(user);
|
||||
}
|
||||
this.currentRoleIds = defaultRoleIds;
|
||||
} catch (PrivilegeException e) {
|
||||
LOG.warn("Set current role fail : {}", e.getMessage());
|
||||
}
|
||||
this.currentRoleIds = defaultRoleIds;
|
||||
} catch (PrivilegeException e) {
|
||||
LOG.warn("Set current role fail : {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -513,6 +534,10 @@ public class ConnectContext {
|
|||
this.currentRoleIds = roleIds;
|
||||
}
|
||||
|
||||
public void setCurrentRoleIds(UserIdentity userIdentity, Set<String> groups) {
|
||||
setCurrentRoleIds(userIdentity);
|
||||
}
|
||||
|
||||
public void setAuthInfoFromThrift(TAuthInfo authInfo) {
|
||||
if (authInfo.isSetCurrent_user_ident()) {
|
||||
setAuthInfoFromThrift(authInfo.getCurrent_user_ident());
|
||||
|
|
@ -571,6 +596,14 @@ public class ConnectContext {
|
|||
return authDataSalt;
|
||||
}
|
||||
|
||||
public String getSecurityIntegration() {
|
||||
return securityIntegration;
|
||||
}
|
||||
|
||||
public void setSecurityIntegration(String securityIntegration) {
|
||||
this.securityIntegration = securityIntegration;
|
||||
}
|
||||
|
||||
public void modifySystemVariable(SystemVariable setVar, boolean onlySetSessionVar) throws DdlException {
|
||||
globalStateMgr.getVariableMgr().setSystemVariable(sessionVariable, setVar, onlySetSessionVar);
|
||||
if (!SetType.GLOBAL.equals(setVar.getType()) && globalStateMgr.getVariableMgr()
|
||||
|
|
@ -1010,6 +1043,7 @@ public class ConnectContext {
|
|||
/**
|
||||
* Get the current compute resource, acquire it if not set.
|
||||
* NOTE: This method will acquire compute resource if it is not set.
|
||||
*
|
||||
* @return: the current compute resource, or the default resource if not in shared data mode.
|
||||
*/
|
||||
public ComputeResource getCurrentComputeResource() {
|
||||
|
|
@ -1025,6 +1059,7 @@ public class ConnectContext {
|
|||
/**
|
||||
* Get the name of the current compute resource.
|
||||
* NOTE: this method will not acquire compute resource if it is not set.
|
||||
*
|
||||
* @return: the name of the current compute resource, or empty string if not set.
|
||||
*/
|
||||
public String getCurrentComputeResourceName() {
|
||||
|
|
@ -1037,6 +1072,7 @@ public class ConnectContext {
|
|||
|
||||
/**
|
||||
* Get the current compute resource without acquiring it.
|
||||
*
|
||||
* @return: the current compute resource(null if not set), or the default resource if not in shared data mode.
|
||||
*/
|
||||
public ComputeResource getCurrentComputeResourceNoAcquire() {
|
||||
|
|
@ -1181,7 +1217,8 @@ public class ConnectContext {
|
|||
/**
|
||||
* NOTE: The ExecTimeout should not contain the pending time which may be caused by QueryQueue's scheduler.
|
||||
* </p>
|
||||
* @return Get the timeout for this session, unit: second
|
||||
*
|
||||
* @return Get the timeout for this session, unit: second
|
||||
*/
|
||||
public int getExecTimeout() {
|
||||
return pendingTimeSecond + getExecTimeoutWithoutPendingTime();
|
||||
|
|
@ -1193,6 +1230,7 @@ public class ConnectContext {
|
|||
|
||||
/**
|
||||
* update the pending time for this session, unit: second
|
||||
*
|
||||
* @param pendingTimeSecond: the pending time for this session
|
||||
*/
|
||||
public void setPendingTimeSecond(int pendingTimeSecond) {
|
||||
|
|
@ -1213,6 +1251,7 @@ public class ConnectContext {
|
|||
|
||||
/**
|
||||
* Check the connect context is timeout or not. If true, kill the connection, otherwise, return false.
|
||||
*
|
||||
* @param now : current time in milliseconds
|
||||
* @return true if timeout, false otherwise
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,16 +12,20 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
package com.starrocks.qe;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.starrocks.authentication.AuthenticationHandler;
|
||||
import com.starrocks.authentication.UserProperty;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.sql.ast.ExecuteAsStmt;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ExecuteAsExecutor {
|
||||
private static final Logger LOG = LogManager.getLogger(ExecuteAsStmt.class);
|
||||
|
||||
|
|
@ -29,7 +33,7 @@ public class ExecuteAsExecutor {
|
|||
* Only set current user, won't reset any other context, for example, current database.
|
||||
* Because mysql client still think that this session is using old databases and will show such hint,
|
||||
* which will only confuse the user
|
||||
*
|
||||
* <p>
|
||||
* MySQL [test_priv]> execute as test1 with no revert;
|
||||
* Query OK, 0 rows affected (0.00 sec)
|
||||
* MySQL [test_priv]> select * from test_table2;
|
||||
|
|
@ -40,14 +44,69 @@ public class ExecuteAsExecutor {
|
|||
Preconditions.checkArgument(!stmt.isAllowRevert());
|
||||
LOG.info("{} EXEC AS {} from now on", ctx.getCurrentUserIdentity(), stmt.getToUser());
|
||||
|
||||
UserIdentity user = stmt.getToUser();
|
||||
ctx.setCurrentUserIdentity(user);
|
||||
ctx.setCurrentRoleIds(user);
|
||||
UserIdentity userIdentity = stmt.getToUser();
|
||||
ctx.setCurrentUserIdentity(userIdentity);
|
||||
|
||||
if (!user.isEphemeral()) {
|
||||
// Refresh groups and roles for all users based on security integration
|
||||
refreshGroupsAndRoles(ctx, userIdentity);
|
||||
|
||||
if (!userIdentity.isEphemeral()) {
|
||||
UserProperty userProperty = ctx.getGlobalStateMgr().getAuthenticationMgr()
|
||||
.getUserProperty(user.getUser());
|
||||
.getUserProperty(userIdentity.getUser());
|
||||
ctx.updateByUserProperty(userProperty);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh groups and roles for user based on security integration
|
||||
* This applies to all users (both external and native) to ensure proper permission refresh
|
||||
*/
|
||||
private static void refreshGroupsAndRoles(ConnectContext ctx, UserIdentity userIdentity) {
|
||||
try {
|
||||
// Get group provider list based on security integration
|
||||
List<String> groupProviderList = getGroupProviderList(ctx);
|
||||
|
||||
// Query groups for the user
|
||||
Set<String> groups = AuthenticationHandler.getGroups(userIdentity, userIdentity.getUser(), groupProviderList);
|
||||
|
||||
// Set groups to context
|
||||
ctx.setGroups(groups);
|
||||
|
||||
// Refresh current role IDs based on user + groups
|
||||
ctx.setCurrentRoleIds(userIdentity);
|
||||
|
||||
LOG.info("Refreshed groups {} and roles for user {}", groups, userIdentity);
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to refresh groups and roles for user {}: {}", userIdentity, e.getMessage());
|
||||
// Continue execution even if group refresh fails
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group provider list based on security integration
|
||||
*/
|
||||
private static List<String> getGroupProviderList(ConnectContext ctx) {
|
||||
String securityIntegration = ctx.getSecurityIntegration();
|
||||
|
||||
// If no security integration is set, use default group provider
|
||||
if (securityIntegration == null || securityIntegration.isEmpty() ||
|
||||
securityIntegration.equals("native")) {
|
||||
return List.of(Config.group_provider);
|
||||
}
|
||||
|
||||
// Try to get group provider from security integration
|
||||
try {
|
||||
var authMgr = ctx.getGlobalStateMgr().getAuthenticationMgr();
|
||||
var si = authMgr.getSecurityIntegration(securityIntegration);
|
||||
if (si != null && si.getGroupProviderName() != null && !si.getGroupProviderName().isEmpty()) {
|
||||
return si.getGroupProviderName();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Failed to get group provider from security integration {}: {}",
|
||||
securityIntegration, e.getMessage());
|
||||
}
|
||||
|
||||
// Fallback to default group provider
|
||||
return List.of(Config.group_provider);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
// 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.authentication;
|
||||
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.persist.EditLog;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.analyzer.Analyzer;
|
||||
import com.starrocks.sql.ast.CreateUserStmt;
|
||||
import com.starrocks.sql.ast.UserAuthOption;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import com.starrocks.sql.parser.NodePosition;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyShort;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
public class AuthenticationHandlerTest {
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
// Mock EditLog
|
||||
EditLog editLog = spy(new EditLog(null));
|
||||
doNothing().when(editLog).logEdit(anyShort(), any());
|
||||
GlobalStateMgr.getCurrentState().setEditLog(editLog);
|
||||
|
||||
new MockUp<LDAPGroupProvider>() {
|
||||
@Mock
|
||||
public void init() throws DdlException {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLdapDNMappingGroup() throws Exception {
|
||||
AuthenticationMgr authenticationMgr = new AuthenticationMgr();
|
||||
GlobalStateMgr.getCurrentState().setAuthenticationMgr(authenticationMgr);
|
||||
CreateUserStmt stmt = new CreateUserStmt(
|
||||
new UserIdentity("ldap_user", "%"),
|
||||
true,
|
||||
new UserAuthOption("AUTHENTICATION_LDAP_SIMPLE",
|
||||
"uid=ldap_user,ou=company,dc=example,dc=com",
|
||||
false, NodePosition.ZERO),
|
||||
List.of(), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(stmt, new ConnectContext());
|
||||
|
||||
authenticationMgr.createUser(stmt);
|
||||
|
||||
new MockUp<LDAPAuthProvider>() {
|
||||
@Mock
|
||||
private void checkPassword(String dn, String password) throws Exception {
|
||||
// mock: always success
|
||||
}
|
||||
|
||||
@Mock
|
||||
private String findUserDNByRoot(String user) throws Exception {
|
||||
return "uid=test,ou=People,dc=starrocks,dc=com";
|
||||
}
|
||||
};
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(GroupProvider.GROUP_PROVIDER_PROPERTY_TYPE_KEY, "ldap");
|
||||
properties.put(LDAPGroupProvider.LDAP_USER_SEARCH_ATTR, "uid");
|
||||
|
||||
String groupName = "ldap_group_provider";
|
||||
authenticationMgr.replayCreateGroupProvider(groupName, properties);
|
||||
Config.group_provider = new String[] {groupName};
|
||||
LDAPGroupProvider ldapGroupProvider = (LDAPGroupProvider) authenticationMgr.getGroupProvider(groupName);
|
||||
|
||||
Map<String, Set<String>> groups = new HashMap<>();
|
||||
groups.put("ldap_user", Set.of("group1", "group2"));
|
||||
groups.put("uid=ldap_user,ou=company,dc=example,dc=com", Set.of("group3", "group4"));
|
||||
groups.put("u1", Set.of("group5"));
|
||||
groups.put("u2", Set.of("group6"));
|
||||
ldapGroupProvider.setUserToGroupCache(groups);
|
||||
|
||||
ConnectContext context = new ConnectContext();
|
||||
AuthenticationHandler.authenticate(context, "ldap_user", "%", "\0".getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
Assertions.assertEquals("ldap_user", context.getQualifiedUser());
|
||||
Assertions.assertEquals("uid=ldap_user,ou=company,dc=example,dc=com", context.getDistinguishedName());
|
||||
|
||||
Assertions.assertEquals(Set.of("group1", "group2"),
|
||||
ldapGroupProvider.getGroup(context.getCurrentUserIdentity(), context.getDistinguishedName()));
|
||||
|
||||
properties = new HashMap<>();
|
||||
properties.put(GroupProvider.GROUP_PROVIDER_PROPERTY_TYPE_KEY, "ldap");
|
||||
|
||||
groupName = "ldap_group_provider2";
|
||||
authenticationMgr.replayCreateGroupProvider(groupName, properties);
|
||||
Config.group_provider = new String[] {groupName};
|
||||
LDAPGroupProvider ldapGroupProvider2 = (LDAPGroupProvider) authenticationMgr.getGroupProvider(groupName);
|
||||
|
||||
Map<String, Set<String>> groups2 = new HashMap<>();
|
||||
groups2.put("ldap_user", Set.of("group1", "group2"));
|
||||
groups2.put("uid=ldap_user,ou=company,dc=example,dc=com", Set.of("group3", "group4"));
|
||||
groups2.put("u1", Set.of("group5"));
|
||||
groups2.put("u2", Set.of("group6"));
|
||||
ldapGroupProvider2.setUserToGroupCache(groups2);
|
||||
Assertions.assertEquals(Set.of("group3", "group4"),
|
||||
ldapGroupProvider2.getGroup(context.getCurrentUserIdentity(), context.getDistinguishedName()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
// 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.authentication;
|
||||
|
||||
import com.starrocks.authorization.AuthorizationMgr;
|
||||
import com.starrocks.authorization.DefaultAuthorizationProvider;
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.mysql.MysqlPassword;
|
||||
import com.starrocks.persist.EditLog;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
import com.starrocks.qe.ExecuteAsExecutor;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.analyzer.Analyzer;
|
||||
import com.starrocks.sql.ast.CreateRoleStmt;
|
||||
import com.starrocks.sql.ast.CreateUserStmt;
|
||||
import com.starrocks.sql.ast.ExecuteAsStmt;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import com.starrocks.sql.parser.NodePosition;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyShort;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
public class ExecuteAsExecutorTest {
|
||||
private AuthenticationMgr authenticationMgr;
|
||||
private AuthorizationMgr authorizationMgr;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() throws Exception {
|
||||
// Mock EditLog
|
||||
EditLog editLog = spy(new EditLog(null));
|
||||
doNothing().when(editLog).logEdit(anyShort(), any());
|
||||
GlobalStateMgr.getCurrentState().setEditLog(editLog);
|
||||
|
||||
authenticationMgr = new AuthenticationMgr();
|
||||
GlobalStateMgr.getCurrentState().setAuthenticationMgr(authenticationMgr);
|
||||
|
||||
authorizationMgr = new AuthorizationMgr(new DefaultAuthorizationProvider());
|
||||
GlobalStateMgr.getCurrentState().setAuthorizationMgr(authorizationMgr);
|
||||
|
||||
new MockUp<LDAPGroupProvider>() {
|
||||
@Mock
|
||||
public void init() throws DdlException {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(GroupProvider.GROUP_PROVIDER_PROPERTY_TYPE_KEY, "ldap");
|
||||
properties.put(LDAPGroupProvider.LDAP_USER_SEARCH_ATTR, "uid");
|
||||
|
||||
String groupName = "ldap_group_provider";
|
||||
authenticationMgr.replayCreateGroupProvider(groupName, properties);
|
||||
Config.group_provider = new String[] {groupName};
|
||||
LDAPGroupProvider ldapGroupProvider = (LDAPGroupProvider) authenticationMgr.getGroupProvider(groupName);
|
||||
|
||||
Map<String, Set<String>> groups = new HashMap<>();
|
||||
groups.put("impersonate_user", Set.of("group1", "group2"));
|
||||
groups.put("u1", Set.of("group3"));
|
||||
groups.put("u2", Set.of("group4"));
|
||||
ldapGroupProvider.setUserToGroupCache(groups);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteAs() throws Exception {
|
||||
authorizationMgr.createRole(new CreateRoleStmt(List.of("r1"), true, ""));
|
||||
authorizationMgr.createRole(new CreateRoleStmt(List.of("r2"), true, ""));
|
||||
|
||||
CreateUserStmt createUserStmt =
|
||||
new CreateUserStmt(new UserIdentity("impersonate_user", "%"), true, null, List.of(), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, new ConnectContext());
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
createUserStmt = new CreateUserStmt(new UserIdentity("u1", "%"), true, null, List.of("r1"), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, new ConnectContext());
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
createUserStmt = new CreateUserStmt(new UserIdentity("u2", "%"), true, null, List.of("r2"), Map.of(), NodePosition.ZERO);
|
||||
Analyzer.analyze(createUserStmt, new ConnectContext());
|
||||
authenticationMgr.createUser(createUserStmt);
|
||||
|
||||
long roleId1 = authorizationMgr.getRoleIdByNameAllowNull("r1");
|
||||
long roleId2 = authorizationMgr.getRoleIdByNameAllowNull("r2");
|
||||
|
||||
// login as impersonate_user
|
||||
|
||||
ConnectContext context = new ConnectContext();
|
||||
AuthenticationHandler.authenticate(context, "impersonate_user", "%", MysqlPassword.EMPTY_PASSWORD);
|
||||
|
||||
Assertions.assertEquals("impersonate_user", context.getQualifiedUser());
|
||||
Assertions.assertEquals(Set.of("group1", "group2"), context.getGroups());
|
||||
|
||||
ExecuteAsStmt executeAsStmt = new ExecuteAsStmt(new UserIdentity("u1", "%"), false);
|
||||
ExecuteAsExecutor.execute(executeAsStmt, context);
|
||||
Assertions.assertEquals(Set.of("group3"), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(roleId1), context.getCurrentRoleIds());
|
||||
|
||||
ExecuteAsStmt executeAsStmt2 = new ExecuteAsStmt(new UserIdentity("u2", "%"), false);
|
||||
ExecuteAsExecutor.execute(executeAsStmt2, context);
|
||||
Assertions.assertEquals(Set.of("group4"), context.getGroups());
|
||||
Assertions.assertEquals(Set.of(roleId2), context.getCurrentRoleIds());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// 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.authentication;
|
||||
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.common.DdlException;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class FileGroupProviderTest {
|
||||
@Test
|
||||
public void testFileGroupProvider() throws DdlException {
|
||||
new MockUp<FileGroupProvider>() {
|
||||
@Mock
|
||||
public InputStream getPath(String groupFileUrl) throws IOException {
|
||||
String path = ClassLoader.getSystemClassLoader().getResource("auth").getPath() + "/" + "file_group";
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
};
|
||||
|
||||
AuthenticationMgr authenticationMgr = GlobalStateMgr.getCurrentState().getAuthenticationMgr();
|
||||
String groupName = "file_group_provider";
|
||||
Map<String, String> properties = Map.of(GroupProvider.GROUP_PROVIDER_PROPERTY_TYPE_KEY, "file",
|
||||
FileGroupProvider.GROUP_FILE_URL, "file_group");
|
||||
|
||||
authenticationMgr.replayCreateGroupProvider(groupName, properties);
|
||||
Config.group_provider = new String[] {groupName};
|
||||
FileGroupProvider fileGroupProvider = (FileGroupProvider) authenticationMgr.getGroupProvider(groupName);
|
||||
|
||||
Set<String> groups = fileGroupProvider.getGroup(new UserIdentity("harbor", "%"), "harbor");
|
||||
Assertions.assertTrue(groups.contains("group1"));
|
||||
Assertions.assertTrue(groups.contains("group2"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// 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.authentication;
|
||||
|
||||
import com.starrocks.common.Config;
|
||||
import com.starrocks.persist.EditLog;
|
||||
import com.starrocks.qe.ConnectContext;
|
||||
import com.starrocks.server.GlobalStateMgr;
|
||||
import com.starrocks.sql.ast.CreateUserStmt;
|
||||
import com.starrocks.sql.ast.UserAuthOption;
|
||||
import com.starrocks.sql.ast.UserIdentity;
|
||||
import com.starrocks.sql.parser.NodePosition;
|
||||
import mockit.Mock;
|
||||
import mockit.MockUp;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyShort;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
class LDAPAuthProviderTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
new MockUp<LDAPAuthProvider>() {
|
||||
@Mock
|
||||
private void checkPassword(String dn, String password) throws Exception {
|
||||
// mock: always success
|
||||
}
|
||||
|
||||
@Mock
|
||||
private String findUserDNByRoot(String user) throws Exception {
|
||||
return "uid=test,ou=People,dc=starrocks,dc=com";
|
||||
}
|
||||
};
|
||||
|
||||
// Mock EditLog
|
||||
EditLog editLog = spy(new EditLog(null));
|
||||
doNothing().when(editLog).logEdit(anyShort(), any());
|
||||
GlobalStateMgr.getCurrentState().setEditLog(editLog);
|
||||
|
||||
AuthenticationMgr authenticationMgr = new AuthenticationMgr();
|
||||
GlobalStateMgr.getCurrentState().setAuthenticationMgr(authenticationMgr);
|
||||
|
||||
authenticationMgr.createUser(new CreateUserStmt(
|
||||
new UserIdentity("ldap_user", "%"),
|
||||
true,
|
||||
new UserAuthOption("AUTHENTICATION_LDAP_SIMPLE",
|
||||
"uid=ldap_user,ou=company,dc=example,dc=com",
|
||||
false, NodePosition.ZERO),
|
||||
List.of(), Map.of(), NodePosition.ZERO));
|
||||
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(GroupProvider.GROUP_PROVIDER_PROPERTY_TYPE_KEY, "file");
|
||||
properties.put(FileGroupProvider.GROUP_FILE_URL, "file_group");
|
||||
|
||||
String groupName = "file_group_provider";
|
||||
authenticationMgr.replayCreateGroupProvider(groupName, properties);
|
||||
Config.group_provider = new String[] {groupName};
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void teardown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticateSetsDNWhenLdapUserDNProvided() throws Exception {
|
||||
|
||||
String providedDN = "cn=test,ou=People,dc=starrocks,dc=com";
|
||||
LDAPAuthProvider provider = new LDAPAuthProvider(
|
||||
"localhost", 389, false,
|
||||
null, null,
|
||||
"cn=admin,dc=starrocks,dc=com", "secret",
|
||||
"ou=People,dc=starrocks,dc=com", "uid",
|
||||
providedDN);
|
||||
|
||||
ConnectContext context = new ConnectContext();
|
||||
UserIdentity user = UserIdentity.createEphemeralUserIdent("ldap_user", "%");
|
||||
byte[] authResponse = "password\0".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
provider.authenticate(context, user, authResponse);
|
||||
Assertions.assertEquals(providedDN, context.getDistinguishedName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAuthenticateSetsDNWhenFindByRoot() throws Exception {
|
||||
String discoveredDN = "uid=test,ou=People,dc=starrocks,dc=com";
|
||||
LDAPAuthProvider provider = new LDAPAuthProvider(
|
||||
"localhost", 389, false,
|
||||
null, null,
|
||||
"cn=admin,dc=starrocks,dc=com", "secret",
|
||||
"ou=People,dc=starrocks,dc=com", "uid",
|
||||
/* ldapUserDN */ null
|
||||
);
|
||||
|
||||
ConnectContext context = new ConnectContext();
|
||||
UserIdentity user = UserIdentity.createEphemeralUserIdent("ldap_user", "%");
|
||||
byte[] authResponse = "password\0".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
provider.authenticate(context, user, authResponse);
|
||||
Assertions.assertEquals(discoveredDN, context.getDistinguishedName());
|
||||
}
|
||||
}
|
||||
|
|
@ -51,7 +51,6 @@ import java.nio.charset.StandardCharsets;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyShort;
|
||||
|
|
@ -127,27 +126,6 @@ public class SecurityIntegrationTest {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFileGroupProvider() throws DdlException, AuthenticationException, IOException, NoSuchMethodException {
|
||||
new MockUp<FileGroupProvider>() {
|
||||
@Mock
|
||||
public InputStream getPath(String groupFileUrl) throws IOException {
|
||||
String path = ClassLoader.getSystemClassLoader().getResource("auth").getPath() + "/" + "file_group";
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
};
|
||||
|
||||
String groupName = "g1";
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
properties.put(FileGroupProvider.GROUP_FILE_URL, "file_group");
|
||||
FileGroupProvider fileGroupProvider = new FileGroupProvider(groupName, properties);
|
||||
fileGroupProvider.init();
|
||||
|
||||
Set<String> groups = fileGroupProvider.getGroup(new UserIdentity("harbor", "127.0.0.1"));
|
||||
Assertions.assertTrue(groups.contains("group1"));
|
||||
Assertions.assertTrue(groups.contains("group2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupProvider() throws Exception {
|
||||
GlobalStateMgr.getCurrentState().setJwkMgr(new MockTokenUtils.MockJwkMgr());
|
||||
|
|
@ -261,7 +239,6 @@ public class SecurityIntegrationTest {
|
|||
Assert.assertTrue(
|
||||
resultSet.getResultRows().get(0).get(1).contains("\"authentication_ldap_simple_bind_root_pwd\" = \"***\""));
|
||||
|
||||
|
||||
properties = new HashMap<>();
|
||||
properties.put(SecurityIntegration.SECURITY_INTEGRATION_PROPERTY_TYPE_KEY, "authentication_oauth2");
|
||||
properties.put(OAuth2AuthenticationProvider.OAUTH2_CLIENT_SECRET, "123");
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
group1:harbor,tina
|
||||
group1:harbor,test
|
||||
group2:harbor
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// 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.authentication;
|
||||
|
||||
import com.starrocks.catalog.UserIdentity;
|
||||
|
||||
/**
|
||||
* AuthenticationContext encapsulates authentication and authorization information for a connection session.
|
||||
* This includes user identity, roles, groups, and authentication-related metadata.
|
||||
*/
|
||||
public class AuthenticationContext {
|
||||
// `qualifiedUser` is the user used when the user establishes connection and authentication.
|
||||
// It is the real user used for this connection.
|
||||
// Different from the `currentUserIdentity` authentication user of execute as,
|
||||
// `qualifiedUser` should not be changed during the entire session.
|
||||
private String qualifiedUser;
|
||||
|
||||
// `currentUserIdentity` is the user used for authorization. Under normal circumstances,
|
||||
// `currentUserIdentity` and `qualifiedUser` are the same user,
|
||||
// but currentUserIdentity may be modified by execute as statement.
|
||||
private UserIdentity currentUserIdentity;
|
||||
|
||||
// Distinguished name (DN) used for LDAP authentication and group resolution
|
||||
// In LDAP context, this represents the unique identifier of a user in the directory
|
||||
// For non-LDAP authentication, this typically defaults to the username
|
||||
// Used by group providers to resolve user group memberships
|
||||
protected String distinguishedName = "";
|
||||
|
||||
// The Token in the OpenIDConnect authentication method is obtained
|
||||
// from the authentication logic and stored in the AuthenticationContext.
|
||||
// If the downstream system needs it, it needs to be obtained from the AuthenticationContext.
|
||||
private volatile String authToken = null;
|
||||
|
||||
// The security integration method used for authentication.
|
||||
protected String securityIntegration = "native";
|
||||
|
||||
// The authentication provider used for this authentication.
|
||||
private AuthenticationProvider authenticationProvider = null;
|
||||
|
||||
// After negotiate and switching with the client,
|
||||
// the auth plugin type used for this authentication is finally determined.
|
||||
private String authPlugin = null;
|
||||
|
||||
// Auth Data salt generated at mysql negotiate used for password salting
|
||||
private byte[] authDataSalt = null;
|
||||
|
||||
public AuthenticationContext() {
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
public String getQualifiedUser() {
|
||||
return qualifiedUser;
|
||||
}
|
||||
|
||||
public void setQualifiedUser(String qualifiedUser) {
|
||||
this.qualifiedUser = qualifiedUser;
|
||||
}
|
||||
|
||||
public UserIdentity getCurrentUserIdentity() {
|
||||
return currentUserIdentity;
|
||||
}
|
||||
|
||||
public void setCurrentUserIdentity(UserIdentity currentUserIdentity) {
|
||||
this.currentUserIdentity = currentUserIdentity;
|
||||
}
|
||||
|
||||
public void setDistinguishedName(String distinguishedName) {
|
||||
this.distinguishedName = distinguishedName;
|
||||
}
|
||||
|
||||
public String getDistinguishedName() {
|
||||
return distinguishedName;
|
||||
}
|
||||
|
||||
public String getAuthToken() {
|
||||
return authToken;
|
||||
}
|
||||
|
||||
public void setAuthToken(String authToken) {
|
||||
this.authToken = authToken;
|
||||
}
|
||||
|
||||
public AuthenticationProvider getAuthenticationProvider() {
|
||||
return authenticationProvider;
|
||||
}
|
||||
|
||||
public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
|
||||
this.authenticationProvider = authenticationProvider;
|
||||
}
|
||||
|
||||
public String getAuthPlugin() {
|
||||
return authPlugin;
|
||||
}
|
||||
|
||||
public void setAuthPlugin(String authPlugin) {
|
||||
this.authPlugin = authPlugin;
|
||||
}
|
||||
|
||||
public byte[] getAuthDataSalt() {
|
||||
return authDataSalt;
|
||||
}
|
||||
|
||||
public void setAuthDataSalt(byte[] authDataSalt) {
|
||||
this.authDataSalt = authDataSalt;
|
||||
}
|
||||
|
||||
public String getSecurityIntegration() {
|
||||
return securityIntegration;
|
||||
}
|
||||
|
||||
public void setSecurityIntegration(String securityIntegration) {
|
||||
this.securityIntegration = securityIntegration;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue