Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public final class DataNodeMiscMessages {

public static final String CREATE_NEW_REGION_ERROR_FMT = "create new region %s error, exception:%s";
public static final String CREATE_NEW_REGION_SUCCEED_FMT = "create new region %s succeed";
public static final String LOG_USER_ARG_ROLE_ARG_422D48D3 = "user: %s, role: %s";
private DataNodeMiscMessages() {}

// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ public final class DataNodeMiscMessages {

public static final String CREATE_NEW_REGION_ERROR_FMT = "创建新 region %s 错误,异常:%s";
public static final String CREATE_NEW_REGION_SUCCEED_FMT = "创建新 region %s 成功";
public static final String LOG_USER_ARG_ROLE_ARG_422D48D3 = "用户:%s,角色:%s";
private DataNodeMiscMessages() {}

// ---------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.queryengine.common.SessionInfo;
import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
import org.apache.iotdb.db.i18n.DataNodeMiscMessages;
import org.apache.iotdb.db.queryengine.plan.Coordinator;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType;
Expand Down Expand Up @@ -213,6 +214,30 @@ public void logRevokeFailure(
status);
}

public void logUserRoleModificationAuthorizationFailure(
Statement statement, IAuditEntity auditEntity, @Nullable TSStatus status) {
if (isSuccessful(status)) {
return;
}
logUserRoleModification(getUserRoleTarget(statement), auditEntity, status);
}

public void logUserRoleModification(
Statement statement,
SessionInfo sessionInfo,
@Nullable String sql,
@Nullable TSStatus status) {
logUserRoleModification(getUserRoleTarget(statement), sessionInfo, sql, status);
}

public void logUserRoleModification(
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement statement,
SessionInfo sessionInfo,
@Nullable String sql,
@Nullable TSStatus status) {
logUserRoleModification(getUserRoleTarget(statement), sessionInfo, sql, status);
}

public void logRevokeFailure(
Statement statement,
SessionInfo sessionInfo,
Expand Down Expand Up @@ -272,6 +297,65 @@ private void logRevokeFailure(
() -> targetName);
}

private void logUserRoleModification(
@Nullable UserRoleTarget target,
@Nullable SessionInfo sessionInfo,
@Nullable String sql,
@Nullable TSStatus status) {
if (target == null || sessionInfo == null || isRedirected(status)) {
return;
}
logUserRoleModification(
target,
sessionInfo.getUserId(),
sessionInfo.getUserName(),
sessionInfo.getCliHostname(),
sessionInfo.getDatabaseName().orElse(null),
sql,
status);
}

private void logUserRoleModification(
@Nullable UserRoleTarget target, IAuditEntity auditEntity, @Nullable TSStatus status) {
if (target == null || isRedirected(status)) {
return;
}
logUserRoleModification(
target,
auditEntity.getUserId(),
auditEntity.getUsername(),
auditEntity.getCliHostname(),
auditEntity.getDatabase(),
auditEntity.getSqlString(),
status);
}

private void logUserRoleModification(
UserRoleTarget target,
long userId,
String username,
String clientAddress,
@Nullable String database,
@Nullable String sql,
@Nullable TSStatus status) {
log(
new AuditLogFields(
userId,
username,
clientAddress,
AuditEventType.MODIFY_ROLE_MEMBERSHIP,
AuditLogOperation.CONTROL,
PrivilegeType.SECURITY,
isSuccessful(status),
database,
sql),
() ->
String.format(
DataNodeMiscMessages.LOG_USER_ARG_ROLE_ARG_422D48D3,
target.username,
target.roleName));
}

@Nullable
private static String getTargetName(Statement statement) {
if (!(statement instanceof AuthorStatement)) {
Expand Down Expand Up @@ -314,12 +398,54 @@ private static String getTargetName(
return null;
}

@Nullable
private static UserRoleTarget getUserRoleTarget(Statement statement) {
if (!(statement instanceof AuthorStatement)) {
return null;
}
AuthorStatement authorStatement = (AuthorStatement) statement;
if (authorStatement.getAuthorType() != AuthorType.GRANT_USER_ROLE
&& authorStatement.getAuthorType() != AuthorType.REVOKE_USER_ROLE) {
return null;
}
return new UserRoleTarget(authorStatement.getUserName(), authorStatement.getRoleName());
}

@Nullable
private static UserRoleTarget getUserRoleTarget(
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement statement) {
if (!(statement instanceof RelationalAuthorStatement)) {
return null;
}
RelationalAuthorStatement authorStatement = (RelationalAuthorStatement) statement;
if (authorStatement.getAuthorType() != AuthorRType.GRANT_USER_ROLE
&& authorStatement.getAuthorType() != AuthorRType.REVOKE_USER_ROLE) {
return null;
}
return new UserRoleTarget(authorStatement.getUserName(), authorStatement.getRoleName());
}

private static boolean isSuccessful(@Nullable TSStatus status) {
return status != null
&& (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()
|| status.getCode() == TSStatusCode.REDIRECTION_RECOMMEND.getStatusCode());
}

private static boolean isRedirected(@Nullable TSStatus status) {
return status != null && status.getCode() == TSStatusCode.REDIRECTION_RECOMMEND.getStatusCode();
}

private static class UserRoleTarget {

private final String username;
private final String roleName;

private UserRoleTarget(String username, String roleName) {
this.username = username;
this.roleName = roleName;
}
}

private static class DNAuditLoggerHolder {

private static final DNAuditLogger INSTANCE = new DNAuditLogger();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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 org.apache.iotdb.db.audit;

import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.queryengine.common.SessionInfo;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement;
import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType;
import org.apache.iotdb.db.queryengine.plan.statement.AuthorType;
import org.apache.iotdb.db.queryengine.plan.statement.Statement;
import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement;
import org.apache.iotdb.rpc.TSStatusCode;

import javax.annotation.Nullable;

import java.util.concurrent.atomic.AtomicBoolean;

/** Ensures one user-role modification attempt produces at most one execution audit record. */
public final class UserRoleModificationAuditContext {

private static final UserRoleModificationAuditContext EMPTY =
new UserRoleModificationAuditContext(null);

private final AuditLogWriter auditLogWriter;
private final AtomicBoolean logged = new AtomicBoolean(false);

private UserRoleModificationAuditContext(@Nullable AuditLogWriter auditLogWriter) {
this.auditLogWriter = auditLogWriter;
}

public static UserRoleModificationAuditContext forTreeStatement(
Statement statement, @Nullable SessionInfo sessionInfo, @Nullable String sql) {
return forTreeStatement(
statement,
sessionInfo,
status ->
DNAuditLogger.getInstance()
.logUserRoleModification(statement, sessionInfo, sql, status));
}

static UserRoleModificationAuditContext forTreeStatement(
Statement statement, @Nullable SessionInfo sessionInfo, AuditLogWriter auditLogWriter) {
return isUserRoleModification(statement) && sessionInfo != null
? new UserRoleModificationAuditContext(auditLogWriter)
: EMPTY;
}

public static UserRoleModificationAuditContext forTableStatement(
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement statement,
@Nullable SessionInfo sessionInfo,
@Nullable String sql) {
return forTableStatement(
statement,
sessionInfo,
status ->
DNAuditLogger.getInstance()
.logUserRoleModification(statement, sessionInfo, sql, status));
}

static UserRoleModificationAuditContext forTableStatement(
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement statement,
@Nullable SessionInfo sessionInfo,
AuditLogWriter auditLogWriter) {
return isUserRoleModification(statement) && sessionInfo != null
? new UserRoleModificationAuditContext(auditLogWriter)
: EMPTY;
}

public void log(@Nullable TSStatus status) {
if (auditLogWriter == null || isRedirected(status) || !logged.compareAndSet(false, true)) {
return;
}
auditLogWriter.log(status);
}

boolean isEnabled() {
return auditLogWriter != null;
}

private static boolean isUserRoleModification(Statement statement) {
if (!(statement instanceof AuthorStatement)) {
return false;
}
AuthorType type = ((AuthorStatement) statement).getAuthorType();
return type == AuthorType.GRANT_USER_ROLE || type == AuthorType.REVOKE_USER_ROLE;
}

private static boolean isUserRoleModification(
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement statement) {
if (!(statement instanceof RelationalAuthorStatement)) {
return false;
}
AuthorRType type = ((RelationalAuthorStatement) statement).getAuthorType();
return type == AuthorRType.GRANT_USER_ROLE || type == AuthorRType.REVOKE_USER_ROLE;
}

private static boolean isRedirected(@Nullable TSStatus status) {
return status != null && status.getCode() == TSStatusCode.REDIRECTION_RECOMMEND.getStatusCode();
}

@FunctionalInterface
interface AuditLogWriter {

void log(@Nullable TSStatus status);
}
}
Loading