diff --git a/src/share/schema/ob_schema_getter_guard.cpp b/src/share/schema/ob_schema_getter_guard.cpp index 4b26287b2f..e85b4c4bea 100644 --- a/src/share/schema/ob_schema_getter_guard.cpp +++ b/src/share/schema/ob_schema_getter_guard.cpp @@ -3336,6 +3336,45 @@ int ObSchemaGetterGuard::check_user_priv(const ObSessionPrivInfo &session_priv, return ret; } +int ObSchemaGetterGuard::check_single_table_priv_or(const ObSessionPrivInfo &session_priv, + const ObNeedPriv &table_need_priv) +{ + int ret = OB_SUCCESS; + const uint64_t tenant_id = session_priv.tenant_id_; + const uint64_t user_id = session_priv.user_id_; + const ObSchemaMgr *mgr = NULL; + if (OB_INVALID_ID == tenant_id || OB_INVALID_ID == user_id) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("Invalid arguments", "tenant_id", tenant_id, "user_id", user_id, KR(ret)); + } else if (OB_FAIL(check_tenant_schema_guard(tenant_id))) { + LOG_WARN("fail to check tenant schema guard", KR(ret), K(tenant_id), K_(tenant_id)); + } else if (OB_FAIL(check_lazy_guard(tenant_id, mgr))) { + LOG_WARN("fail to check lazy guard", KR(ret), K(tenant_id)); + } else if (OB_PRIV_HAS_ANY(session_priv.user_priv_set_, table_need_priv.priv_set_)) { + /* check success */ + } else { + const ObPrivMgr &priv_mgr = mgr->priv_mgr_; + bool pass = false; + if (OB_FAIL(check_priv_db_or_(session_priv, table_need_priv, priv_mgr, tenant_id, user_id, pass))) { + LOG_WARN("failed to check priv db or", K(ret)); + } else if (pass) { + /* check success */ + } else if (OB_FAIL(check_priv_table_or_(table_need_priv, priv_mgr, tenant_id, user_id, pass))) { + LOG_WARN("fail to check priv table or", K(ret)); + } else if (pass) { + /* check success */ + } else { + ret = OB_ERR_NO_TABLE_PRIVILEGE; + const char *priv_name = "ANY"; + LOG_USER_ERROR(OB_ERR_NO_TABLE_PRIVILEGE, (int)strlen(priv_name), priv_name, + session_priv.user_name_.length(), session_priv.user_name_.ptr(), + session_priv.host_name_.length(), session_priv.host_name_.ptr(), + table_need_priv.table_.length(), table_need_priv.table_.ptr()); + } + } + return ret; +} + int ObSchemaGetterGuard::check_single_table_priv(const ObSessionPrivInfo &session_priv, const ObNeedPriv &table_need_priv) { @@ -3733,14 +3772,29 @@ int ObSchemaGetterGuard::check_priv(const ObSessionPrivInfo &session_priv, break; } case OB_PRIV_TABLE_LEVEL: { - if (OB_FAIL(check_single_table_priv(session_priv, need_priv))) { - LOG_WARN("No privilege", "tenant_id", session_priv.tenant_id_, - "user_id", session_priv.user_id_, - "need_priv", need_priv.priv_set_, - "table", need_priv.table_, - "db", need_priv.db_, - "user_priv", session_priv.user_priv_set_, - KR(ret));//need print priv + if (OB_PRIV_CHECK_ALL == need_priv.priv_check_type_) { + if (OB_FAIL(check_single_table_priv(session_priv, need_priv))) { + LOG_WARN("No privilege", "tenant_id", session_priv.tenant_id_, + "user_id", session_priv.user_id_, + "need_priv", need_priv.priv_set_, + "table", need_priv.table_, + "db", need_priv.db_, + "user_priv", session_priv.user_priv_set_, + KR(ret));//need print priv + } + } else if (OB_PRIV_CHECK_ANY == need_priv.priv_check_type_) { + if (OB_FAIL(check_single_table_priv_or(session_priv, need_priv))) { + LOG_WARN("No privilege", "tenant_id", session_priv.tenant_id_, + "user_id", session_priv.user_id_, + "need_priv", need_priv.priv_set_, + "table", need_priv.table_, + "db", need_priv.db_, + "user_priv", session_priv.user_priv_set_, + KR(ret)); + } + } else { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("Privilege checking of other not use this function yet", KR(ret)); } break; } @@ -3832,10 +3886,7 @@ int ObSchemaGetterGuard::check_priv_table_or_(const ObNeedPriv &need_priv, need_priv.table_); if (OB_FAIL(priv_mgr.get_table_priv(table_priv_key, table_priv))) { LOG_WARN("get table priv failed", KR(ret), K(table_priv_key)); - } else if (OB_ISNULL(table_priv)) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("table priv is null", KR(ret), K(table_priv_key)); - } else { + } else if (NULL != table_priv) { table_priv_set = table_priv->get_priv_set(); } diff --git a/src/share/schema/ob_schema_getter_guard.h b/src/share/schema/ob_schema_getter_guard.h index 2b6936f326..fa389609f1 100644 --- a/src/share/schema/ob_schema_getter_guard.h +++ b/src/share/schema/ob_schema_getter_guard.h @@ -496,6 +496,8 @@ public: bool print_warn = true); int check_single_table_priv(const ObSessionPrivInfo &session_priv, const ObNeedPriv &table_need_priv); + int check_single_table_priv_or(const ObSessionPrivInfo &session_priv, + const ObNeedPriv &table_need_priv); int get_session_priv_info(const uint64_t tenant_id, const uint64_t user_id, const ObString &database_name, diff --git a/src/share/schema/ob_schema_struct.cpp b/src/share/schema/ob_schema_struct.cpp index a82d7d878e..579c22ccb4 100644 --- a/src/share/schema/ob_schema_struct.cpp +++ b/src/share/schema/ob_schema_struct.cpp @@ -8963,6 +8963,7 @@ int ObNeedPriv::deep_copy(const ObNeedPriv &other, common::ObIAllocator &allocat priv_set_ = other.priv_set_; is_sys_table_ = other.is_sys_table_; is_for_update_ = other.is_for_update_; + priv_check_type_ = other.priv_check_type_; if (OB_FAIL(ob_write_string(allocator, other.db_, db_))) { LOG_WARN("Fail to deep copy db", K_(db), K(ret)); } else if (OB_FAIL(ob_write_string(allocator, other.table_, table_))) { diff --git a/src/share/schema/ob_schema_struct.h b/src/share/schema/ob_schema_struct.h index 1e622e1f46..e6870b6efd 100755 --- a/src/share/schema/ob_schema_struct.h +++ b/src/share/schema/ob_schema_struct.h @@ -4630,6 +4630,13 @@ enum ObPrivLevel OB_PRIV_MAX_LEVEL, }; +enum ObPrivCheckType +{ + OB_PRIV_CHECK_ALL, + OB_PRIV_CHECK_ANY, + OB_PRIV_CHECK_OTHER, +}; + const char *ob_priv_level_str(const ObPrivLevel grant_level); struct ObNeedPriv @@ -4639,13 +4646,14 @@ struct ObNeedPriv ObPrivLevel priv_level, ObPrivSet priv_set, const bool is_sys_table, - const bool is_for_update = false) + const bool is_for_update = false, + ObPrivCheckType priv_check_type = OB_PRIV_CHECK_ALL) : db_(db), table_(table), priv_level_(priv_level), priv_set_(priv_set), - is_sys_table_(is_sys_table), is_for_update_(is_for_update) + is_sys_table_(is_sys_table), is_for_update_(is_for_update), priv_check_type_(priv_check_type) { } ObNeedPriv() : db_(), table_(), priv_level_(OB_PRIV_INVALID_LEVEL), priv_set_(0), is_sys_table_(false), - is_for_update_(false) + is_for_update_(false), priv_check_type_(OB_PRIV_CHECK_ALL) { } int deep_copy(const ObNeedPriv &other, common::ObIAllocator &allocator); common::ObString db_; @@ -4654,7 +4662,9 @@ struct ObNeedPriv ObPrivSet priv_set_; bool is_sys_table_; // May be used to represent the table of schema metadata bool is_for_update_; - TO_STRING_KV(K_(db), K_(table), K_(priv_set), K_(priv_level), K_(is_sys_table), K_(is_for_update)); + ObPrivCheckType priv_check_type_; + TO_STRING_KV(K_(db), K_(table), K_(priv_set), K_(priv_level), K_(is_sys_table), K_(is_for_update), + K_(priv_check_type)); }; struct ObStmtNeedPrivs diff --git a/src/sql/resolver/ddl/ob_create_view_resolver.cpp b/src/sql/resolver/ddl/ob_create_view_resolver.cpp index 191e55f229..241a04c0ab 100644 --- a/src/sql/resolver/ddl/ob_create_view_resolver.cpp +++ b/src/sql/resolver/ddl/ob_create_view_resolver.cpp @@ -400,7 +400,165 @@ int ObCreateViewResolver::check_view_columns(ObSelectStmt &select_stmt, return ret; } -// fix me: should do privilege check for table items in select_stmt recursively +// get all tables/view in subquery. +int ObCreateViewResolver::get_sel_priv_tables_in_subquery(const ObSelectStmt *select_stmt, + hash::ObHashMap &select_tables) +{ + int ret = OB_SUCCESS; + ObString info_schema(OB_INFORMATION_SCHEMA_NAME); + if (NULL != select_stmt) { + for (int64_t i = 0; OB_SUCC(ret) && i < select_stmt->get_table_items().count(); i++) { + const TableItem *table_item = select_stmt->get_table_item(i); + const TableItem *dummy_item = NULL; + if (OB_ISNULL(table_item)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("table_item is NULL ptr", K(ret)); + } else if (!(table_item->is_basic_table() || table_item->is_view_table_) || + (table_item->database_name_.empty() && !table_item->dblink_name_.empty())) { + /* do nothing */ + } else if (OB_FAIL(select_tables.get_refactored(table_item->ref_id_, dummy_item))) { + if (OB_HASH_NOT_EXIST != ret) { + LOG_WARN("failed to get refactor", K(ret)); + } else { + ret = OB_SUCCESS; + bool is_database_name_equal = false; + if (OB_FAIL(ObResolverUtils::name_case_cmp(session_info_, table_item->database_name_, + info_schema, OB_TABLE_NAME_CLASS, + is_database_name_equal))) { + } else if (is_database_name_equal) { + //do nothing + } else if (OB_FAIL(select_tables.set_refactored(table_item->ref_id_, table_item))) { + LOG_WARN("failed to set refacted", K(ret)); + } + } + } + } + if (OB_SUCC(ret)) { + // subquery + generated table in child_stmts + ObSEArray child_stmts; + if (OB_FAIL(select_stmt->get_child_stmts(child_stmts))) { + LOG_WARN("get child stmt failed", K(ret)); + } else { + for (int64_t i = 0; OB_SUCC(ret) && i < child_stmts.count(); i++) { + if (OB_ISNULL(child_stmts.at(i))) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("child stmt is NULL", K(ret)); + } else if (SMART_CALL(get_sel_priv_tables_in_subquery(child_stmts.at(i), select_tables))) { + LOG_WARN("failed to get need privs in child stmt", K(ret)); + } + } + } + } + } + return ret; +} + +/* select (expr or sub_query) from (table/view/generated table) elsewhere (expr or subquery) + * - tables will appear at select expr, select sub_query, from item, elsewhere expr + * - only pure column ref expr to a table/view in select expr need any privileges + * - select sub_query, from item, elsewhere expr need select privileges + */ +int ObCreateViewResolver::get_need_priv_tables(ObSelectStmt &root_stmt, + hash::ObHashMap &select_tables, + hash::ObHashMap &any_tables) +{ + int ret = OB_SUCCESS; + ObRelIds select_table_ids; // select priv tables in root_stmt + ObString info_schema(OB_INFORMATION_SCHEMA_NAME); + const TableItem *dummy_item = NULL; + // 1. get tables in select_item, need select privilege + for (int64_t i = 0; OB_SUCC(ret) && i < root_stmt.get_select_item_size(); i++) { + const ObRawExpr *expr = root_stmt.get_select_item(i).expr_; + if (OB_ISNULL(expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get NULL ptr", K(ret)); + } else if (!expr->is_column_ref_expr()) { + if (OB_FAIL(select_table_ids.add_members(expr->get_relation_ids()))) { + LOG_WARN("failed to add members", K(ret)); + } else { /* do nothing */ } + } + } + // 2. get tables in elsewhere, need select privilege + if (OB_SUCC(ret)) { + ObSEArray else_exprs; // exprs in elsewhere + ObStmtExprGetter visitor; + visitor.set_relation_scope(); + visitor.remove_scope(SCOPE_SELECT); + visitor.set_recursive(false); + if (OB_FAIL(root_stmt.get_relation_exprs(else_exprs, visitor))) { + LOG_WARN("failed to get relation exprs", K(ret)); + } else { + for (int64_t i = 0; OB_SUCC(ret) && i < else_exprs.count(); i++) { + const ObRawExpr *expr = else_exprs.at(i); + if (OB_ISNULL(expr)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("get NULL ptr", K(ret)); + } else if (OB_FAIL(select_table_ids.add_members(expr->get_relation_ids()))) { + LOG_WARN("failed to add members", K(ret)); + } else { /* do nothing */ } + } + } + } + if (OB_SUCC(ret)) { + // subquery + generated table in child_stmts + ObSEArray child_stmts; + if (OB_FAIL(root_stmt.get_child_stmts(child_stmts))) { + LOG_WARN("get child stmt failed", K(ret)); + } else { + for (int64_t i = 0; OB_SUCC(ret) && i < child_stmts.count(); i++) { + if (OB_ISNULL(child_stmts.at(i))) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("child stmt is NULL", K(ret)); + } else if (SMART_CALL(get_sel_priv_tables_in_subquery(child_stmts.at(i), select_tables))) { + LOG_WARN("failed to get need privs in child stmt", K(ret)); + } + } + } + } + // 4. get tables at from_scope tables/views, need any privileges + if (OB_SUCC(ret)) { + for (int64_t i = 0; OB_SUCC(ret) && i < root_stmt.get_table_items().count(); i++) { + const TableItem *table_item = root_stmt.get_table_item(i); + bool is_database_name_equal = false; + if (OB_ISNULL(table_item)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("table item is null"); + } else if (!(table_item->is_basic_table() || table_item->is_view_table_) || + (table_item->database_name_.empty() && !table_item->dblink_name_.empty())) { + /* do nothing */ + } else if (OB_FAIL(select_tables.get_refactored(table_item->ref_id_, dummy_item))) { + if (OB_HASH_NOT_EXIST != ret) { + LOG_WARN("failed to get refactored", K(ret)); + } else { + ret = OB_SUCCESS; + if (OB_FAIL(ObResolverUtils::name_case_cmp(session_info_, table_item->database_name_, + info_schema, OB_TABLE_NAME_CLASS, is_database_name_equal))) { + } else if (is_database_name_equal) { + /* do nothing */ + } else if (select_table_ids.has_member(root_stmt.get_table_bit_index(table_item->table_id_))) { + if (OB_FAIL(select_tables.set_refactored(table_item->ref_id_, table_item))) { + LOG_WARN("failed to set refactor", K(ret)); + } + } else { + if (OB_FAIL(any_tables.get_refactored(table_item->ref_id_, dummy_item))) { + if (OB_HASH_NOT_EXIST != ret) { + LOG_WARN("failed to get refactored", K(ret)); + } else { + ret = OB_SUCCESS; + if (OB_FAIL(any_tables.set_refactored(table_item->ref_id_, table_item))) { + LOG_WARN("failed to set refactor", K(ret)); + } + } + } + } + } + } else { /* do nothing */ } + } + } + return ret; +} + +// MySQL5.7 privilege check refrence https://dev.mysql.com/doc/refman/5.7/en/create-view.html int ObCreateViewResolver::check_privilege_needed(ObCreateTableStmt &stmt, ObSelectStmt &select_stmt, const bool is_force_view) @@ -408,61 +566,95 @@ int ObCreateViewResolver::check_privilege_needed(ObCreateTableStmt &stmt, int ret = OB_SUCCESS; const bool is_sys_table = false; int64_t table_size = select_stmt.get_table_size(); - int64_t need_privs_size = is_force_view ? 2 : table_size + 2; - ObNeedPriv need_priv(stmt.get_database_name(), stmt.get_table_name(), - OB_PRIV_TABLE_LEVEL, OB_PRIV_DROP, is_sys_table); + common::hash::ObHashMap select_tables; + common::hash::ObHashMap any_tables; + ObNeedPriv need_priv(stmt.get_database_name(), stmt.get_table_name(), OB_PRIV_TABLE_LEVEL, + OB_PRIV_DROP, is_sys_table); if (OB_ISNULL(session_info_)) { ret = OB_ERR_UNEXPECTED; LOG_WARN("session_info_ should not be NULL", K(ret)); - } else if (OB_FAIL(stmt.get_view_need_privs().reserve(need_privs_size))) { - LOG_WARN("fail to reserve view need privs array", K(ret)); - } else if (stmt.get_create_table_arg().if_not_exist_ - && OB_FAIL(stmt.add_view_need_priv(need_priv))) { - LOG_WARN("Fail to add need_priv", K(ret)); - } else if (OB_FALSE_IT(need_priv.priv_set_ = OB_PRIV_CREATE_VIEW)) { - } else if (OB_FAIL(stmt.add_view_need_priv(need_priv))) { - LOG_WARN("Fail to add need_priv", K(ret)); - } else if (is_force_view) { - /* do not add table to check privilege for force view */ - } else { - for (int64_t i = 0; OB_SUCC(ret) && i < table_size; ++i) { - ObString database_name; - ObString table_name; - const TableItem *table_item = select_stmt.get_table_item(i); - if (OB_ISNULL(table_item)) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("table item is null"); - } else if (table_item->is_basic_table() || table_item->is_view_table_) { - //no check for information_schema select - ObString info_schema("information_schema"); - bool is_table_name_equal = false; - if (OB_FAIL(ObResolverUtils::name_case_cmp(session_info_, table_item->database_name_, - info_schema, OB_TABLE_NAME_CLASS, - is_table_name_equal))) { - } else if (is_table_name_equal) { - //do nothing - } else if (table_item->database_name_.empty() && !table_item->dblink_name_.empty()) { - //do nothing - } else if (OB_FAIL(ob_write_string(*allocator_, - table_item->database_name_, - database_name))) { - LOG_WARN("Write string database name error", K(ret)); - } else if (OB_FAIL(ob_write_string(*allocator_, - table_item->table_name_, - table_name))) { - LOG_WARN("Write table name error", K(table_item->table_name_), K(ret)); - } else { - need_priv.db_ = database_name; - need_priv.table_ = table_name; - need_priv.priv_set_ = OB_PRIV_SELECT; - need_priv.is_sys_table_ = table_item->is_system_table_; - need_priv.is_for_update_ = table_item->for_update_; - need_priv.priv_level_ = OB_PRIV_TABLE_LEVEL; - if (OB_FAIL(stmt.add_view_need_priv(need_priv))) { - LOG_WARN("Fail to add need_priv", K(ret), K(need_priv)); + } else if (OB_FAIL(select_tables.create(8, "DDLResolver"))) { + LOG_WARN("failed to create a hashmap", K(ret)); + } else if (OB_FAIL(any_tables.create(8, "DDLResolver"))) { + LOG_WARN("failed to create a hashmap", K(ret)); + } else if (!is_force_view && + OB_FAIL(get_need_priv_tables(select_stmt, select_tables, any_tables))) { + LOG_WARN("failed to get need priv tables", K(ret)); + } + if (OB_SUCC(ret)) { + int64_t need_privs_size = is_force_view ? 2 : 2 + select_tables.size() + any_tables.size(); + if (OB_FAIL(stmt.get_view_need_privs().reserve(need_privs_size))) { + LOG_WARN("fail to reserve view need privs array", K(ret)); + } else if (stmt.get_create_table_arg().if_not_exist_ && + OB_FAIL(stmt.add_view_need_priv(need_priv))) { + LOG_WARN("Fail to add need_priv", K(ret)); + } else if (OB_FALSE_IT(need_priv.priv_set_ = OB_PRIV_CREATE_VIEW)) { + } else if (OB_FAIL(stmt.add_view_need_priv(need_priv))) { + LOG_WARN("Fail to add need_priv", K(ret)); + } else if (!is_force_view) { + if (!any_tables.empty()) { + hash::ObHashMap::iterator iter = any_tables.begin(); + for (; OB_SUCC(ret) && iter != any_tables.end(); iter++) { + ObString database_name; + ObString table_name; + const TableItem *table_item = iter->second; + if (OB_ISNULL(table_item)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("table item is null"); + } else if (OB_FAIL(ob_write_string(*allocator_, table_item->database_name_, + database_name))) { + LOG_WARN("Write string database name error", K(ret)); + } else if (OB_FAIL(ob_write_string(*allocator_, table_item->table_name_, table_name))) { + LOG_WARN("Write table name error", K(table_item->table_name_), K(ret)); + } else { + ObNeedPriv need_priv_else(database_name, table_name, OB_PRIV_TABLE_LEVEL, + OB_PRIV_SELECT | OB_PRIV_INSERT | OB_PRIV_UPDATE | OB_PRIV_DELETE, + table_item->is_system_table_, table_item->for_update_, + OB_PRIV_CHECK_ANY); + if (OB_FAIL(stmt.add_view_need_priv(need_priv_else))) { + LOG_WARN("Fail to add need_priv", K(ret), K(need_priv_else)); + } } } } + if (OB_SUCC(ret) && !select_tables.empty()) { + hash::ObHashMap::iterator iter = select_tables.begin(); + for (; OB_SUCC(ret) && iter != select_tables.end(); iter++) { + ObString database_name; + ObString table_name; + const TableItem *table_item = iter->second; + if (OB_ISNULL(table_item)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("table item is null"); + } else if (OB_FAIL(ob_write_string(*allocator_, table_item->database_name_, + database_name))) { + LOG_WARN("Write string database name error", K(ret)); + } else if (OB_FAIL(ob_write_string(*allocator_, table_item->table_name_, table_name))) { + LOG_WARN("Write table name error", K(table_item->table_name_), K(ret)); + } else { + ObNeedPriv need_priv_else(database_name, table_name, OB_PRIV_TABLE_LEVEL, + OB_PRIV_SELECT, table_item->is_system_table_, + table_item->for_update_); + if (OB_FAIL(stmt.add_view_need_priv(need_priv_else))) { + LOG_WARN("Fail to add need_priv", K(ret), K(need_priv_else)); + } + } + } + } + } + } + if (select_tables.created()) { + int tmp_ret = OB_SUCCESS; + if (OB_UNLIKELY(OB_SUCCESS != (tmp_ret = select_tables.destroy()))) { + LOG_WARN("failed to destroy select_tables map", K(tmp_ret)); + ret = COVER_SUCC(tmp_ret); + } + } + if (any_tables.created()) { + int tmp_ret = OB_SUCCESS; + if (OB_UNLIKELY(OB_SUCCESS != (tmp_ret = any_tables.destroy()))) { + LOG_WARN("failed to destroy any_tables map", K(tmp_ret)); + ret = COVER_SUCC(tmp_ret); } } if (OB_SUCC(ret) && ObSchemaChecker::is_ora_priv_check()) { diff --git a/src/sql/resolver/ddl/ob_create_view_resolver.h b/src/sql/resolver/ddl/ob_create_view_resolver.h index 227dfe1cee..070fa314ce 100644 --- a/src/sql/resolver/ddl/ob_create_view_resolver.h +++ b/src/sql/resolver/ddl/ob_create_view_resolver.h @@ -89,6 +89,11 @@ private: common::ObString &expanded_view); int collect_dependency_infos(ObQueryCtx *query_ctx, obrpc::ObCreateTableArg &create_arg); + int get_sel_priv_tables_in_subquery(const ObSelectStmt *child_stmt, + hash::ObHashMap &select_tables); + int get_need_priv_tables(ObSelectStmt &select_stmt, + hash::ObHashMap &select_tables, + hash::ObHashMap &any_tables); private: DISALLOW_COPY_AND_ASSIGN(ObCreateViewResolver);