github-actions[bot] commented on code in PR #67885: URL: https://github.com/apache/doris/pull/67885#discussion_r4014681485
########## fe/fe-core/src/main/java/org/apache/doris/nereids/SecurityDependencyContext.java: ########## @@ -0,0 +1,217 @@ +// 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.doris.nereids; + +import org.apache.doris.analysis.UserIdentity; +import org.apache.doris.authorization.DataMaskSpec; +import org.apache.doris.authorization.RowFilterSpec; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.OlapTable; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.datasource.CatalogIf; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.InternalAuthorizationPlugin; +import org.apache.doris.nereids.rules.analysis.UserAuthentication; +import org.apache.doris.policy.PolicyMgr; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.SessionVariable; + +import com.google.common.collect.ImmutableSet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** Security dependencies of a reusable prepared point-query plan. */ +public class SecurityDependencyContext { + private final UserIdentity planningUserIdentity; + private final Set<String> planningAuthenticatedRoles; + private final Env planningEnv; + private final boolean authorizationChecksEnabled; + private final List<CheckedPrivilege> checkedPrivileges = new ArrayList<>(); + private boolean hasEffectiveRowPolicy; + private boolean hasDataMask; + private boolean complete; + + /** Create an incomplete context for tests and callers without a connection. */ + public SecurityDependencyContext() { + this(null, ImmutableSet.of(), null, false); + } + + /** Capture the authorization subject before analysis starts. */ + public SecurityDependencyContext(ConnectContext connectContext) { + this(connectContext == null ? null : connectContext.getCurrentUserIdentity(), + authenticatedRoles(connectContext), + connectContext == null ? null : connectContext.getEnv(), + usesAuthorizationChecks(connectContext)); + } + + private SecurityDependencyContext(UserIdentity planningUserIdentity, Set<String> planningAuthenticatedRoles, + Env planningEnv, boolean authorizationChecksEnabled) { + this.planningUserIdentity = planningUserIdentity; + this.planningAuthenticatedRoles = planningAuthenticatedRoles; + this.planningEnv = planningEnv; + this.authorizationChecksEnabled = authorizationChecksEnabled; + this.complete = authorizationChecksEnabled; + } + + /** Record the exact SELECT check which must be repeated before direct reuse. */ + public synchronized void addCheckedPrivilege(TableIf table, Set<String> usedColumns) { + if (table == null) { + complete = false; + return; + } + DatabaseIf<?> database = table.getDatabase(); + CatalogIf<?> catalog = database == null ? null : database.getCatalog(); + if (catalog == null + || !(table instanceof OlapTable) + || !InternalCatalog.INTERNAL_CATALOG_NAME.equals(catalog.getName())) { + complete = false; + return; + } + checkedPrivileges.add(new CheckedPrivilege(table, database, catalog, catalog.getName(), + database.getFullName(), table.getName(), + usedColumns == null ? ImmutableSet.of() : ImmutableSet.copyOf(usedColumns))); + } + + /** Record mask presence so a masked plan is never reused without policy analysis. */ + public synchronized void addDataMask( + String catalog, String database, String table, String column, Optional<DataMaskSpec> mask) { + hasDataMask |= mask.isPresent(); + } + + public synchronized boolean hasDataMask() { + return hasDataMask; + } + + /** Record only whether external policy analysis produced a row filter; definitions are not retained. */ + public synchronized void addRowPolicies(List<RowFilterSpec> policies) { + hasEffectiveRowPolicy |= policies != null && !policies.isEmpty(); + } + + public synchronized boolean hasEffectiveRowPolicy() { + return hasEffectiveRowPolicy; + } + + /** Freeze the completed dependency set before storing it with the prepared plan. */ + public synchronized SecurityDependencyContext snapshotForShortCircuit() { + SecurityDependencyContext snapshot = new SecurityDependencyContext( + planningUserIdentity, planningAuthenticatedRoles, planningEnv, authorizationChecksEnabled); + snapshot.checkedPrivileges.addAll(checkedPrivileges); + snapshot.hasEffectiveRowPolicy = hasEffectiveRowPolicy; + snapshot.hasDataMask = hasDataMask; + snapshot.complete = complete && !checkedPrivileges.isEmpty() + && !hasEffectiveRowPolicy && !hasDataMask + && checkedPrivileges.stream().allMatch(CheckedPrivilege::matchesNamespace); + return snapshot; + } + + /** + * Recheck the small set of facts needed to bypass planning. Returning false only rejects direct reuse; normal + * planning then performs the authoritative check and reports its standard error. + */ + public boolean isValid(ConnectContext connectContext) { Review Comment: [P1] Preserve the relation namespace across security refresh For an unqualified prepared query, execute `SELECT ... FROM t` in `db_a` to publish this cache, then run `USE db_b`. While the security facts stay unchanged, this check still passes and direct execution remains on `db_a.t`. If a privilege, role, or row-policy change now makes the new security dependency fail, `refreshPreparedPlan` reparses the one-part name under the current connection and `BindRelation` binds `db_b.t`, permanently switching the same handle to a different table. Thus a security invalidation determines relation resolution. Capture the prepare/bound resolution namespace and use it during refresh (or make cached and normal executions consistently follow the chosen Doris semantics), and add a two-database `USE` plus policy/revoke regression. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
