diff --git a/fe/fe-core/src/main/java/com/starrocks/alter/AlterJobExecutor.java b/fe/fe-core/src/main/java/com/starrocks/alter/AlterJobExecutor.java index b363d7e5abd1cf..126271f8f8f974 100644 --- a/fe/fe-core/src/main/java/com/starrocks/alter/AlterJobExecutor.java +++ b/fe/fe-core/src/main/java/com/starrocks/alter/AlterJobExecutor.java @@ -35,6 +35,7 @@ import com.starrocks.catalog.RangePartitionInfo; import com.starrocks.catalog.Table; import com.starrocks.catalog.Type; +import com.starrocks.catalog.View; import com.starrocks.common.AnalysisException; import com.starrocks.common.DdlException; import com.starrocks.common.ErrorCode; @@ -205,6 +206,12 @@ public Void visitAlterViewStatement(AlterViewStmt statement, ConnectContext cont this.db = db; this.table = table; + + if (statement.getAlterClause() == null) { + ((View) table).setSecurity(statement.isSecurity()); + return null; + } + AlterViewClause alterViewClause = (AlterViewClause) statement.getAlterClause(); visit(alterViewClause, context); return null; diff --git a/fe/fe-core/src/main/java/com/starrocks/authorization/ColumnPrivilege.java b/fe/fe-core/src/main/java/com/starrocks/authorization/ColumnPrivilege.java index b65adafde6335d..4b3c8a949e79b7 100644 --- a/fe/fe-core/src/main/java/com/starrocks/authorization/ColumnPrivilege.java +++ b/fe/fe-core/src/main/java/com/starrocks/authorization/ColumnPrivilege.java @@ -16,6 +16,7 @@ import com.google.common.collect.Maps; import com.starrocks.analysis.TableName; +import com.starrocks.catalog.BasicTable; import com.starrocks.catalog.Column; import com.starrocks.catalog.InternalCatalog; import com.starrocks.catalog.Table; @@ -23,6 +24,7 @@ import com.starrocks.catalog.system.SystemTable; import com.starrocks.connector.metadata.MetadataTable; import com.starrocks.qe.ConnectContext; +import com.starrocks.server.GlobalStateMgr; import com.starrocks.sql.StatementPlanner; import com.starrocks.sql.analyzer.Authorizer; import com.starrocks.sql.ast.AstTraverser; @@ -147,6 +149,18 @@ public static void check(ConnectContext context, QueryStatement stmt, List allTables = view.getTableRefs(); + for (TableName t : allTables) { + BasicTable basicTable = GlobalStateMgr.getCurrentState().getMetadataMgr().getBasicTable( + InternalCatalog.DEFAULT_INTERNAL_CATALOG_NAME, t.getDb(), t.getTbl()); + + Authorizer.checkAnyActionOnTableLikeObject(context.getCurrentUserIdentity(), + null, t.getDb(), basicTable); + } + } + Authorizer.checkViewAction(context.getCurrentUserIdentity(), context.getCurrentRoleIds(), tableName, PrivilegeType.SELECT); } diff --git a/fe/fe-core/src/main/java/com/starrocks/catalog/View.java b/fe/fe-core/src/main/java/com/starrocks/catalog/View.java index 61793a78aeb3eb..a7caf904ac0c14 100644 --- a/fe/fe-core/src/main/java/com/starrocks/catalog/View.java +++ b/fe/fe-core/src/main/java/com/starrocks/catalog/View.java @@ -86,6 +86,9 @@ public class View extends Table { @SerializedName(value = "m") private long sqlMode = 0L; + @SerializedName(value = "s") + private boolean security = false; + // cache used table names private List tableRefsCache = Lists.newArrayList(); @@ -136,6 +139,14 @@ public long getSqlMode() { return sqlMode; } + public void setSecurity(boolean security) { + this.security = security; + } + + public boolean isSecurity() { + return security; + } + /** * Initializes the originalViewDef, inlineViewDef, and queryStmt members * by parsing the expanded view definition SQL-string. diff --git a/fe/fe-core/src/main/java/com/starrocks/server/LocalMetastore.java b/fe/fe-core/src/main/java/com/starrocks/server/LocalMetastore.java index 4eedb8163b7222..f8f9e11c338256 100644 --- a/fe/fe-core/src/main/java/com/starrocks/server/LocalMetastore.java +++ b/fe/fe-core/src/main/java/com/starrocks/server/LocalMetastore.java @@ -4242,6 +4242,11 @@ public void createView(CreateViewStmt stmt) throws DdlException { view.setInlineViewDefWithSqlMode(stmt.getInlineViewDef(), ConnectContext.get().getSessionVariable().getSqlMode()); // init here in case the stmt string from view.toSql() has some syntax error. + + if (stmt.isSecurity()) { + view.setSecurity(stmt.isSecurity()); + } + try { view.init(); } catch (StarRocksException e) { diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/analyzer/ViewAnalyzer.java b/fe/fe-core/src/main/java/com/starrocks/sql/analyzer/ViewAnalyzer.java index 1d1df0f360a3d5..6725d700eca829 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/analyzer/ViewAnalyzer.java +++ b/fe/fe-core/src/main/java/com/starrocks/sql/analyzer/ViewAnalyzer.java @@ -80,6 +80,10 @@ public Void visitAlterViewStatement(AlterViewStmt stmt, ConnectContext context) throw new SemanticException("The specified table [" + tableName + "] is not a view"); } + if (stmt.getAlterClause() == null) { + return null; + } + AlterClause alterClause = stmt.getAlterClause(); AlterViewClause alterViewClause = (AlterViewClause) alterClause; diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/ast/AlterViewStmt.java b/fe/fe-core/src/main/java/com/starrocks/sql/ast/AlterViewStmt.java index 80def814b45011..eaee17cd61c853 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/ast/AlterViewStmt.java +++ b/fe/fe-core/src/main/java/com/starrocks/sql/ast/AlterViewStmt.java @@ -20,11 +20,13 @@ // Alter view statement public class AlterViewStmt extends DdlStmt { private final TableName tableName; + private final boolean security; private final AlterClause alterClause; - public AlterViewStmt(TableName tableName, AlterClause alterClause, NodePosition pos) { + public AlterViewStmt(TableName tableName, boolean security, AlterClause alterClause, NodePosition pos) { super(pos); this.tableName = tableName; + this.security = security; this.alterClause = alterClause; } @@ -34,13 +36,17 @@ public static AlterViewStmt fromReplaceStmt(CreateViewStmt stmt) { alterViewClause.setInlineViewDef(stmt.getInlineViewDef()); alterViewClause.setColumns(stmt.getColumns()); alterViewClause.setComment(stmt.getComment()); - return new AlterViewStmt(stmt.getTableName(), alterViewClause, NodePosition.ZERO); + return new AlterViewStmt(stmt.getTableName(), stmt.isSecurity(), alterViewClause, NodePosition.ZERO); } public TableName getTableName() { return tableName; } + public boolean isSecurity() { + return security; + } + public AlterClause getAlterClause() { return alterClause; } diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/ast/CreateViewStmt.java b/fe/fe-core/src/main/java/com/starrocks/sql/ast/CreateViewStmt.java index a8fd68fb1a4914..5cb67743871dde 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/ast/CreateViewStmt.java +++ b/fe/fe-core/src/main/java/com/starrocks/sql/ast/CreateViewStmt.java @@ -27,15 +27,19 @@ public class CreateViewStmt extends DdlStmt { private final boolean ifNotExists; private final boolean replace; private final String comment; + private final boolean security; protected QueryStatement queryStatement; //Resolved by Analyzer protected List columns; private String inlineViewDef; - public CreateViewStmt(boolean ifNotExists, boolean replace, - TableName tableName, List colWithComments, + public CreateViewStmt(boolean ifNotExists, + boolean replace, + TableName tableName, + List colWithComments, String comment, + boolean security, QueryStatement queryStmt, NodePosition pos) { super(pos); @@ -44,6 +48,7 @@ public CreateViewStmt(boolean ifNotExists, boolean replace, this.tableName = tableName; this.colWithComments = colWithComments; this.comment = Strings.nullToEmpty(comment); + this.security = security; this.queryStatement = queryStmt; } @@ -79,6 +84,10 @@ public String getComment() { return comment; } + public boolean isSecurity() { + return security; + } + public QueryStatement getQueryStatement() { return queryStatement; } diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/parser/AstBuilder.java b/fe/fe-core/src/main/java/com/starrocks/sql/parser/AstBuilder.java index 1d7ebd2304ff80..281df72d0b80e1 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/parser/AstBuilder.java +++ b/fe/fe-core/src/main/java/com/starrocks/sql/parser/AstBuilder.java @@ -1616,12 +1616,23 @@ public ParseNode visitCreateViewStatement(StarRocksParser.CreateViewStatementCon throw new ParsingException(PARSER_ERROR_MSG.conflictedOptions("if not exists", "or replace"), createPos(context)); } + + boolean isSecurity = false; + if (context.SECURITY() != null) { + if (context.NONE() != null) { + isSecurity = false; + } else if (context.INVOKER() != null) { + isSecurity = true; + } + } + return new CreateViewStmt( context.IF() != null, context.REPLACE() != null, targetTableName, colWithComments, context.comment() == null ? null : ((StringLiteral) visit(context.comment())).getStringValue(), + isSecurity, (QueryStatement) visit(context.queryStatement()), createPos(context)); } @@ -1631,13 +1642,25 @@ public ParseNode visitAlterViewStatement(StarRocksParser.AlterViewStatementConte TableName targetTableName = qualifiedNameToTableName(qualifiedName); List colWithComments = null; - if (context.columnNameWithComment().size() > 0) { + if (!context.columnNameWithComment().isEmpty()) { colWithComments = visit(context.columnNameWithComment(), ColWithComment.class); } - QueryStatement queryStatement = (QueryStatement) visit(context.queryStatement()); - AlterClause alterClause = new AlterViewClause(colWithComments, queryStatement, createPos(context)); - return new AlterViewStmt(targetTableName, alterClause, createPos(context)); + boolean isSecurity = false; + if (context.SECURITY() != null) { + if (context.NONE() != null) { + isSecurity = false; + } else if (context.INVOKER() != null) { + isSecurity = true; + } + + return new AlterViewStmt(targetTableName, isSecurity, null, createPos(context)); + } else { + QueryStatement queryStatement = (QueryStatement) visit(context.queryStatement()); + AlterClause alterClause = new AlterViewClause(colWithComments, queryStatement, createPos(context)); + + return new AlterViewStmt(targetTableName, isSecurity, alterClause, createPos(context)); + } } @Override diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocks.g4 b/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocks.g4 index cd736caedf390f..07ba111c159cfe 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocks.g4 +++ b/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocks.g4 @@ -607,12 +607,15 @@ recoverPartitionStatement createViewStatement : CREATE (OR REPLACE)? VIEW (IF NOT EXISTS)? qualifiedName - ('(' columnNameWithComment (',' columnNameWithComment)* ')')? - comment? AS queryStatement + ('(' columnNameWithComment (',' columnNameWithComment)* ')')? + comment? + (SECURITY (NONE | INVOKER))? + AS queryStatement ; alterViewStatement - : ALTER VIEW qualifiedName ('(' columnNameWithComment (',' columnNameWithComment)* ')')? AS queryStatement + : ALTER VIEW qualifiedName ('(' columnNameWithComment (',' columnNameWithComment)* ')')? AS queryStatement + | ALTER VIEW qualifiedName SET SECURITY (NONE | INVOKER) ; dropViewStatement diff --git a/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocksLex.g4 b/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocksLex.g4 index 3bebb0ca736e75..a6ed05a1e28dca 100644 --- a/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocksLex.g4 +++ b/fe/fe-core/src/main/java/com/starrocks/sql/parser/StarRocksLex.g4 @@ -226,6 +226,7 @@ INTERMEDIATE: 'INTERMEDIATE'; INTERSECT: 'INTERSECT'; INTERVAL: 'INTERVAL'; INTO: 'INTO'; +INVOKER: 'INVOKER'; GIN: 'GIN'; OVERWRITE: 'OVERWRITE'; IS: 'IS'; diff --git a/fe/fe-core/src/test/java/com/starrocks/connector/iceberg/IcebergMetadataTest.java b/fe/fe-core/src/test/java/com/starrocks/connector/iceberg/IcebergMetadataTest.java index 43fd2434fc0da9..49358b31f711d9 100644 --- a/fe/fe-core/src/test/java/com/starrocks/connector/iceberg/IcebergMetadataTest.java +++ b/fe/fe-core/src/test/java/com/starrocks/connector/iceberg/IcebergMetadataTest.java @@ -1491,7 +1491,7 @@ public void testCreateView(@Mocked RESTCatalog restCatalog, @Mocked BaseView bas }; CreateViewStmt stmt = new CreateViewStmt(false, false, new TableName("catalog", "db", "table"), - Lists.newArrayList(new ColWithComment("k1", "", NodePosition.ZERO)), "", null, NodePosition.ZERO); + Lists.newArrayList(new ColWithComment("k1", "", NodePosition.ZERO)), "", false, null, NodePosition.ZERO); stmt.setColumns(Lists.newArrayList(new Column("k1", INT))); metadata.createView(stmt); diff --git a/test/sql/test_alter_view/R/test_alter_view b/test/sql/test_view/R/test_alter_view similarity index 100% rename from test/sql/test_alter_view/R/test_alter_view rename to test/sql/test_view/R/test_alter_view diff --git a/test/sql/test_view/R/test_security_view b/test/sql/test_view/R/test_security_view new file mode 100644 index 00000000000000..9372eddbf9b256 --- /dev/null +++ b/test/sql/test_view/R/test_security_view @@ -0,0 +1,105 @@ +-- name: test_security_view +create table t1(c1 bigint, c2 bigint); +-- result: +-- !result +create table t2(c3 bigint, c4 bigint); +-- result: +-- !result +create view v1 as select * from t1, t2; +-- result: +-- !result +create view v2 security invoker as select * from t1, t2; +-- result: +-- !result +create user if not exists u1; +-- result: +-- !result +grant impersonate on user root to u1; +-- result: +-- !result +grant select on view v1 to user u1; +-- result: +-- !result +grant select on view v2 to user u1; +-- result: +-- !result +create user if not exists u2; +-- result: +-- !result +grant impersonate on user root to u2; +-- result: +-- !result +grant select on table t1 to user u2; +-- result: +-- !result +grant select on table t2 to user u2; +-- result: +-- !result +grant select on view v1 to user u2; +-- result: +-- !result +grant select on view v2 to user u2; +-- result: +-- !result +execute as u1 with no revert; +-- result: +-- !result +select * from v1; +-- result: +-- !result +select * from v2; +-- result: +E: (5203, 'Access denied; you need (at least one of) the SELECT privilege(s) on VIEW v2 for this operation. Please ask the admin to grant permission(s) or try activating existing roles using . Current role(s): NONE. Inactivated role(s): NONE.') +-- !result +execute as root with no revert; +-- result: +-- !result +execute as u2 with no revert; +-- result: +-- !result +select * from v1; +-- result: +-- !result +select * from v2; +-- result: +-- !result +execute as root with no revert; +-- result: +-- !result +alter view v1 set security invoker; +-- result: +-- !result +alter view v2 set security none; +-- result: +-- !result +execute as u1 with no revert; +-- result: +-- !result +select * from v1; +-- result: +E: (5203, 'Access denied; you need (at least one of) the SELECT privilege(s) on VIEW v1 for this operation. Please ask the admin to grant permission(s) or try activating existing roles using . Current role(s): NONE. Inactivated role(s): NONE.') +-- !result +select * from v2; +-- result: +-- !result +execute as root with no revert; +-- result: +-- !result +execute as u2 with no revert; +-- result: +-- !result +select * from v1; +-- result: +-- !result +select * from v2; +-- result: +-- !result +execute as root with no revert; +-- result: +-- !result +drop user u1; +-- result: +-- !result +drop user u2; +-- result: +-- !result \ No newline at end of file diff --git a/test/sql/test_alter_view/T/test_alter_view b/test/sql/test_view/T/test_alter_view similarity index 100% rename from test/sql/test_alter_view/T/test_alter_view rename to test/sql/test_view/T/test_alter_view diff --git a/test/sql/test_view/T/test_security_view b/test/sql/test_view/T/test_security_view new file mode 100644 index 00000000000000..b926495defd4b8 --- /dev/null +++ b/test/sql/test_view/T/test_security_view @@ -0,0 +1,45 @@ +-- name: test_security_view + +create table t1(c1 bigint, c2 bigint); +create table t2(c3 bigint, c4 bigint); + +create view v1 as select * from t1, t2; +create view v2 security invoker as select * from t1, t2; + +create user if not exists u1; +grant impersonate on user root to u1; +grant select on view v1 to user u1; +grant select on view v2 to user u1; + +create user if not exists u2; +grant impersonate on user root to u2; +grant select on table t1 to user u2; +grant select on table t2 to user u2; +grant select on view v1 to user u2; +grant select on view v2 to user u2; + +execute as u1 with no revert; +select * from v1; +select * from v2; +execute as root with no revert; + +execute as u2 with no revert; +select * from v1; +select * from v2; +execute as root with no revert; + +alter view v1 set security invoker; +alter view v2 set security none; + +execute as u1 with no revert; +select * from v1; +select * from v2; +execute as root with no revert; + +execute as u2 with no revert; +select * from v1; +select * from v2; +execute as root with no revert; + +drop user u1; +drop user u2; \ No newline at end of file