← Back to team overview

maria-developers team mailing list archive

Re: PLEASE REVIEW: (MDEV-7574) Security definer views don't work with CONNECT ODBC tables

 

Hi Sergei,


On 02/19/2015 07:12 PM, Sergei Golubchik wrote:
Hi, Alexander!

On Feb 17, Alexander Barkov wrote:

 From what I understood, FILE_ACL is written (among the other
privileges) into thd->security_ctx.privilege in
TABLE_LIST::prepare_security(). In case of a DEFINER view,
thd->security_ctx.privilege is filled exactly with the definer
privileges, and to the invoker privileges otherwise.

So inside ha_connect::check_privileges() the fact that there is
FILE_ACL in thd->security_ctx.privilege means that
TABLE_LIST::prepare_security() was previously called and FILE_ACL is
set to DEFINER or INVOKER, according to the view definition. This is
exactly what we need.

Agree, looks good so far :)

I'm not sure about the opposite: if there is no FILE_ACL in
thd->security_ctx.privilege, what does it mean?  Does it mean that
there is no FILE_ACL for the effective user?  Or can it also mean that
TABLE_LIST::prepare_security() was not called?

As far as I can see, TABLE_LIST::prepare_security() is always called for
a view that was successfully opened. That is, no FILE_ACL bit means that
there is no FILE privilege.

=== modified file 'storage/connect/ha_connect.cc'
--- storage/connect/ha_connect.cc	2015-01-20 00:21:56 +0000
+++ storage/connect/ha_connect.cc	2015-02-17 13:46:34 +0000
@@ -3865,6 +3865,8 @@ bool ha_connect::check_privileges(THD *t
      case TAB_MAC:
      case TAB_WMI:
      case TAB_OEM:
+      if (table && (table->grant.privilege & FILE_ACL))
+        return false;
        return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);

I don't like that you use both approaches to check the privileges. This
just looks wrong. It should rather be

   * if it's called from ::external_lock() - use table->grant.privilege.
   * otherwise (::create() or ::delete_or_rename_table()) - don't use
     table->grant.privilege, only use check_access().

Something like

    if (called_from_external_lock)
      return table->grant.privilege & FILE_ACL; // respect view's definer
    else
      return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);

Thanks for the suggestion!

Please find a new version attached. It now uses
thd_sql_command() and lock_type to decide whether
to use table->grant.privilege or check_access().


There is only one problem with that. In case of embedded server
table->grant.privilege is always 0, because the embedded version
of check_table_access() is just an empty function.


This change in sql/handler.cc, in handler::ha_external_lock() helps:

+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+  table->grant.privilege= ~NO_ACCESS;
+#endif


With this change "mtr --embedded" passes all tests,
including those I added in this patch.


Possibly, a there is a better way to do this.
Please suggest.

Thanks.


Regards,
Sergei

=== modified file 'sql/handler.cc'
--- sql/handler.cc	2015-01-21 11:03:02 +0000
+++ sql/handler.cc	2015-02-24 11:47:05 +0000
@@ -5873,6 +5873,9 @@ int handler::ha_external_lock(THD *thd,
 
   ha_statistic_increment(&SSV::ha_external_lock_count);
 
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+  table->grant.privilege= ~NO_ACCESS;
+#endif
   /*
     We cache the table flags if the locking succeeded. Otherwise, we
     keep them as they were when they were fetched in ha_open().

=== modified file 'storage/connect/ha_connect.cc'
--- storage/connect/ha_connect.cc	2015-02-11 20:39:41 +0000
+++ storage/connect/ha_connect.cc	2015-02-24 12:03:25 +0000
@@ -3922,7 +3922,21 @@ int ha_connect::delete_all_rows()
 } // end of delete_all_rows
 
 
-bool ha_connect::check_privileges(THD *thd, PTOS options, char *dbn)
+/**
+  Check privileges.
+  @param THD                   - Current thread
+  @param options               - Connect table options
+  @param dbn                   - database name
+  @param using_table_privilege - whether check table->grant.privilege,
+                                 or execute check_access(FILE_ACL).
+
+  Using table->grant.privilege is important in cases when we need to take into
+  account privileges of the VIEW definer when accessing to a view created with
+    "CREATE VIEW v1 SQL SECURITY DEFINER".
+  See ha_connect::check_privileges_external_lock() for details.
+*/
+bool ha_connect::check_privileges(THD *thd, PTOS options,
+                                  char *dbn, bool using_table_privilege)
 {
   const char *db= (dbn && *dbn) ? dbn : NULL;
   TABTYPE     type=GetRealType(options);
@@ -3978,7 +3992,25 @@ bool ha_connect::check_privileges(THD *t
     case TAB_MAC:
     case TAB_WMI:
     case TAB_OEM:
-      return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
+      if (using_table_privilege)
+      {
+        // Called from ::external_lock(), respect VIEW's definer
+        if (table->grant.privilege & FILE_ACL)
+          return false;
+        else
+        {
+          my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+                   thd->security_ctx->priv_user,
+                   thd->security_ctx->priv_host,
+                   (thd->password ?  ER(ER_YES) : ER(ER_NO)));
+          return true;
+        }
+      }
+      else
+      {
+        // Called from ::create() or ::delete_or_rename_table()
+        return check_access(thd, FILE_ACL, db, NULL, NULL, 0, 0);
+      }
 
     // This is temporary until a solution is found
     case TAB_TBL:
@@ -4019,6 +4051,15 @@ bool ha_connect::IsSameIndex(PIXDEF xp1,
   return b;
 } // end of IsSameIndex
 
+
+void ha_connect::report_unsupported_sql_command(PGLOBAL g, THD *thd)
+{
+  htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
+  strcpy(g->Message, "CONNECT Unsupported command");
+  my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
+}
+
+
 MODE ha_connect::CheckMode(PGLOBAL g, THD *thd,
                            MODE newmode, bool *chk, bool *cras)
 {
@@ -4079,9 +4120,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, TH
 //        } // endif partitioned
 
       default:
-        htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
-        strcpy(g->Message, "CONNECT Unsupported command");
-        my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
+        report_unsupported_sql_command(g, thd);
         newmode= MODE_ERROR;
         break;
       } // endswitch newmode
@@ -4095,7 +4134,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, TH
       case SQLCOM_LOAD:
       case SQLCOM_INSERT_SELECT:
 //    case SQLCOM_REPLACE:
-//    case SQLCOM_REPLACE_SELECT:
+      case SQLCOM_REPLACE_SELECT: // REPLACE t1 SELECT * FROM connect_table
       case SQLCOM_DELETE:
       case SQLCOM_DELETE_MULTI:
       case SQLCOM_TRUNCATE:
@@ -4128,9 +4167,7 @@ MODE ha_connect::CheckMode(PGLOBAL g, TH
 //        } // endif partitioned
 
       default:
-        htrc("Unsupported sql_command=%d\n", thd_sql_command(thd));
-        strcpy(g->Message, "CONNECT Unsupported command");
-        my_message(ER_NOT_ALLOWED_COMMAND, g->Message, MYF(0));
+        report_unsupported_sql_command(g, thd);
         newmode= MODE_ERROR;
         break;
       } // endswitch newmode
@@ -4143,6 +4180,67 @@ MODE ha_connect::CheckMode(PGLOBAL g, TH
   return newmode;
 } // end of check_mode
 
+
+/**
+  A check_privilege() wrapper for external_lock().
+  Decides if check_privilege():
+  - should test table->grant.privilege for FILE_ACL
+  - or should call check_access(FILE_ACL)
+  depending on the current SQL command and lock type.
+*/
+bool ha_connect::check_privileges_external_lock(PGLOBAL g, THD *thd,
+                                                PTOS options, int lock_type)
+{
+  bool use_table_priv;
+  switch (thd_sql_command(thd))
+  {
+  case SQLCOM_SELECT:
+  case SQLCOM_UPDATE:
+  case SQLCOM_INSERT:
+  case SQLCOM_DELETE:
+  case SQLCOM_REPLACE:
+  case SQLCOM_LOAD:
+    use_table_priv= true;              // use table->grant.privilege
+    break;
+
+  case SQLCOM_CREATE_TABLE:
+  case SQLCOM_INSERT_SELECT:
+  case SQLCOM_REPLACE_SELECT:
+  case SQLCOM_UPDATE_MULTI:
+  case SQLCOM_DELETE_MULTI:
+    /*
+      CREATE TABLE target_table AS SELECT * FROM source_table;
+      INSERT INTO  target_table SELECT * FROM source_table;
+      REPLACE INTO target_table SELECT * FROM source_table;    
+      UPDATE target_table,source_table SET target_table.column=xxx WHERE ...;
+      DELETE target_table FROM target_table,source_table WHERE ...;
+
+      If we're working with "source_table", use table->grant.privilege.
+      If we're working with "target_table", use check_access().
+    */
+    use_table_priv= lock_type != F_WRLCK;
+    break;
+
+  case SQLCOM_TRUNCATE:
+  case SQLCOM_LOCK_TABLES:
+  case SQLCOM_DROP_TABLE:
+  case SQLCOM_RENAME_TABLE:
+  case SQLCOM_CREATE_VIEW:
+  case SQLCOM_DROP_VIEW:
+  case SQLCOM_ALTER_TABLE:
+  case SQLCOM_DROP_INDEX:
+  case SQLCOM_CREATE_INDEX:
+  case SQLCOM_OPTIMIZE:
+    use_table_priv= false;             // use check_access()
+    break;
+  default:
+    report_unsupported_sql_command(g, thd);
+    return true;                       // Something went wrong, deny access.
+  }
+  return check_privileges(thd, options, table->s->db.str, use_table_priv);
+}
+
+
 int ha_connect::start_stmt(THD *thd, thr_lock_type lock_type)
 {
   int     rc= 0;
@@ -4363,7 +4461,7 @@ int ha_connect::external_lock(THD *thd,
 
   DBUG_ASSERT(table && table->s);
 
-  if (check_privileges(thd, options, table->s->db.str)) {
+  if (check_privileges_external_lock(g, thd, options, lock_type)) {
     strcpy(g->Message, "This operation requires the FILE privilege");
     htrc("%s\n", g->Message);
     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
@@ -4614,7 +4712,7 @@ int ha_connect::delete_or_rename_table(c
     if (!open_table_def(thd, share)) {
       // Now we can work
       if ((pos= share->option_struct)) {
-        if (check_privileges(thd, pos, db))
+        if (check_privileges(thd, pos, db, false))
           rc= HA_ERR_INTERNAL_ERROR;         // ???
         else
           if (IsFileType(GetRealType(pos)) && !pos->filename)
@@ -5592,7 +5690,7 @@ int ha_connect::create(const char *name,
     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
   } // endif ttp
 
-  if (check_privileges(thd, options, GetDBfromName(name)))
+  if (check_privileges(thd, options, GetDBfromName(name), false))
     DBUG_RETURN(HA_ERR_INTERNAL_ERROR);
 
   inward= IsFileType(type) && !options->filename;

=== modified file 'storage/connect/ha_connect.h'
--- storage/connect/ha_connect.h	2015-01-09 22:36:50 +0000
+++ storage/connect/ha_connect.h	2015-02-24 07:31:05 +0000
@@ -530,8 +530,11 @@ int index_prev(uchar *buf);
 private:
   DsMrr_impl ds_mrr;
 
+  void report_unsupported_sql_command(PGLOBAL g, THD *thd);
 protected:
-  bool check_privileges(THD *thd, PTOS options, char *dbn);
+  bool check_privileges(THD *thd, PTOS options, char *dbn,
+                        bool using_table_privilege);
+  bool check_privileges_external_lock(PGLOBAL g, THD *thd, PTOS options, int lock_type);
   MODE CheckMode(PGLOBAL g, THD *thd, MODE newmode, bool *chk, bool *cras);
   char *GetDBfromName(const char *name);
 

=== modified file 'storage/connect/mysql-test/connect/r/grant.result'
--- storage/connect/mysql-test/connect/r/grant.result	2013-05-21 14:29:10 +0000
+++ storage/connect/mysql-test/connect/r/grant.result	2015-02-24 10:17:30 +0000
@@ -43,10 +43,79 @@ ERROR 28000: Access denied for user 'use
 CREATE VIEW v1 AS SELECT * FROM t1;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
 # Testing a VIEW created with FILE privileges but accessed with no FILE
+# Using SQL SECURITY DEFINER
 SELECT user();
 user()
 root@localhost
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1;
+SELECT user();
+user()
+user@localhost
+SELECT fname, ftype FROM v1 ORDER BY fname, ftype;
+fname	ftype
+t1	.frm
+v1	.frm
+INSERT INTO v1 (fname, ftype) VALUES ('aaa','bin');
+ERROR HY000: Got error 174 'COLBLK SetBuffer: undefined Access Method' from CONNECT
+REPLACE INTO v1 (fname, ftype) VALUES ('aaa','bin');
+ERROR 42000: CONNECT Unsupported command
+UPDATE v1 SET fname='aaa';
+ERROR HY000: Got error 174 'COLBLK SetBuffer: undefined Access Method' from CONNECT
+DELETE FROM v1;
+ERROR HY000: Got error 122 'DIR tables are read/only' from CONNECT
+# Testing LOAD DATA for a VIEW with SQL SECURITY DEFINER
+LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1;;
+ERROR HY000: Got error 174 'COLBLK SetBuffer: undefined Access Method' from CONNECT
+LOAD DATA INFILE 'test.txt' INTO TABLE v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+# Testing multiple-table statements
+CREATE TABLE t2 ENGINE=MyISAM AS SELECT fname, ftype FROM v1;
+SELECT * FROM t2 ORDER BY fname, ftype;
+fname	ftype
+t1	.frm
+t2	.frm
+t2	.MYD
+t2	.MYI
+v1	.frm
+DELETE FROM t2;
+ALTER TABLE t2 ADD PRIMARY KEY (fname, ftype);
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+REPLACE INTO t2 SELECT fname, ftype FROM v1;
+SELECT fname,ftype FROM t2 ORDER BY fname, ftype;
+fname	ftype
+t1	.frm
+t2	.frm
+t2	.MYD
+t2	.MYI
+v1	.frm
+ALTER TABLE t2 ADD cnt INT NOT NULL;
+UPDATE t2,v1 SET t2.cnt=1 WHERE t2.fname=v1.fname;
+SELECT fname,ftype FROM t2 ORDER BY fname, ftype;
+fname	ftype
+t1	.frm
+t2	.frm
+t2	.MYD
+t2	.MYI
+v1	.frm
+DELETE t2 FROM t2,v1 WHERE t2.fname=v1.fname AND t2.ftype=v1.ftype;
+SELECT fname,ftype FROM v1 ORDER BY fname,ftype;
+fname	ftype
+t1	.frm
+t2	.frm
+t2	.MYD
+t2	.MYI
+v1	.frm
+DROP TABLE t2;
+DROP VIEW v1;
+#
+# End of tests with SQL SECURITY DEFINER
+#
+# Testing a VIEW created with FILE privileges but accessed with no FILE
+# Using SQL SECURITY INVOKER
+SELECT user();
+user()
+root@localhost
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 SELECT user();
 user()
 user@localhost
@@ -58,6 +127,31 @@ UPDATE v1 SET path=123;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
 DELETE FROM v1;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+# Testing LOAD DATA for a VIEW with SQL SECURITY INVOKER
+LOAD DATA LOCAL INFILE 'MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1;;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+LOAD DATA INFILE 'test.txt' INTO TABLE v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+# Testing multiple-table statements
+CREATE TABLE t2 AS SELECT fname, ftype FROM v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+CREATE TABLE t2 (fname VARCHAR(256) NOT NULL, ftype CHAR(4) NOT NULL) ENGINE=MyISAM;
+INSERT INTO t2 VALUES ('test','bin');
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+REPLACE INTO t2 SELECT fname, ftype FROM v1;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+ALTER TABLE t2 ADD cnt INT NOT NULL;
+UPDATE t2,v1 SET t2.cnt=1 WHERE t2.fname=v1.fname;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+DELETE t2 FROM t2,v1 WHERE t2.fname=v1.fname AND t2.ftype=v1.ftype;
+ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+DROP TABLE t2;
+#
+# End of tests with SQL SECURITY INVOKER
+#
 SELECT user();
 user()
 root@localhost

=== modified file 'storage/connect/mysql-test/connect/r/mysql_grant.result'
--- storage/connect/mysql-test/connect/r/mysql_grant.result	2013-05-21 14:29:10 +0000
+++ storage/connect/mysql-test/connect/r/mysql_grant.result	2015-02-17 13:40:33 +0000
@@ -40,7 +40,7 @@ ERROR 28000: Access denied for user 'use
 SELECT user();
 user()
 root@localhost
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 SELECT user();
 user()
 user@localhost

=== modified file 'storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result'
--- storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result	2013-11-06 17:22:09 +0000
+++ storage/connect/mysql-test/connect/r/odbc_sqlite3_grant.result	2015-02-24 10:30:25 +0000
@@ -49,10 +49,11 @@ ERROR 28000: Access denied for user 'use
 CREATE VIEW v1 AS SELECT * FROM t1;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
 # Testing a VIEW created with FILE privileges but accessed with no FILE
+# using SQL SECIRITY INVOKER
 SELECT user();
 user()
 root@localhost
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 SELECT user();
 user()
 user@localhost
@@ -64,6 +65,19 @@ UPDATE v1 SET a=123;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
 DELETE FROM v1;
 ERROR 28000: Access denied for user 'user'@'localhost' (using password: NO)
+# Testing a VIEW created with FILE privileges but accessed with no FILE
+# using SQL SECIRITY DEFINER
+DROP VIEW v1;
+SELECT user();
+user()
+root@localhost
+CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1;
+SELECT user();
+user()
+user@localhost
+SELECT * FROM v1 WHERE a='test1';
+a
+test1
 SELECT user();
 user()
 root@localhost

=== modified file 'storage/connect/mysql-test/connect/t/grant.test'
--- storage/connect/mysql-test/connect/t/grant.test	2013-05-21 14:29:10 +0000
+++ storage/connect/mysql-test/connect/t/grant.test	2015-02-24 10:19:01 +0000
@@ -47,9 +47,65 @@ ALTER TABLE t1 READONLY=1;
 CREATE VIEW v1 AS SELECT * FROM t1;
 
 --echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--echo # Using SQL SECURITY DEFINER
 --connection default
 SELECT user();
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1;
+--connection user
+SELECT user();
+SELECT fname, ftype FROM v1 ORDER BY fname, ftype;
+
+# The following commands successfully pass access control but then fail
+# because the underlying table does not support data change commands.
+# The important thing is that the commands don't fail on ER_ACCESS_DENIED_ERROR
+
+--error ER_GET_ERRMSG
+INSERT INTO v1 (fname, ftype) VALUES ('aaa','bin');
+--error ER_NOT_ALLOWED_COMMAND
+REPLACE INTO v1 (fname, ftype) VALUES ('aaa','bin');
+--error ER_GET_ERRMSG
+UPDATE v1 SET fname='aaa';
+--error ER_GET_ERRMSG
+DELETE FROM v1;
+
+--echo # Testing LOAD DATA for a VIEW with SQL SECURITY DEFINER
+--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR
+--error ER_GET_ERRMSG
+--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1;
+
+# Non-LOCAL LOAD DATA still fails on ER_ACCESS_DENIED_ERROR, even though
+# the view DEFINER has FILE privilege to access the underlying table data.
+# The reason is that the invoker does not have FILE privilege
+# to access to load the specified server-side *file*.
+--error ER_ACCESS_DENIED_ERROR
+LOAD DATA INFILE 'test.txt' INTO TABLE v1;
+
+--echo # Testing multiple-table statements
+
+CREATE TABLE t2 ENGINE=MyISAM AS SELECT fname, ftype FROM v1;
+SELECT * FROM t2 ORDER BY fname, ftype;
+DELETE FROM t2;
+ALTER TABLE t2 ADD PRIMARY KEY (fname, ftype);
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+REPLACE INTO t2 SELECT fname, ftype FROM v1;
+SELECT fname,ftype FROM t2 ORDER BY fname, ftype;
+ALTER TABLE t2 ADD cnt INT NOT NULL;
+UPDATE t2,v1 SET t2.cnt=1 WHERE t2.fname=v1.fname;
+SELECT fname,ftype FROM t2 ORDER BY fname, ftype;
+DELETE t2 FROM t2,v1 WHERE t2.fname=v1.fname AND t2.ftype=v1.ftype;
+SELECT fname,ftype FROM v1 ORDER BY fname,ftype;
+DROP TABLE t2;
+--connection default
+DROP VIEW v1;
+--echo #
+--echo # End of tests with SQL SECURITY DEFINER
+--echo #
+
+--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--echo # Using SQL SECURITY INVOKER
+--connection default
+SELECT user();
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 --connection user
 SELECT user();
 --error ER_ACCESS_DENIED_ERROR
@@ -61,6 +117,36 @@ UPDATE v1 SET path=123;
 --error ER_ACCESS_DENIED_ERROR
 DELETE FROM v1;
 
+--echo # Testing LOAD DATA for a VIEW with SQL SECURITY INVOKER
+--replace_result $MTR_SUITE_DIR MTR_SUITE_DIR
+--error ER_ACCESS_DENIED_ERROR
+--eval LOAD DATA LOCAL INFILE '$MTR_SUITE_DIR/std_data/boys.txt' INTO TABLE v1;
+--error ER_ACCESS_DENIED_ERROR
+LOAD DATA INFILE 'test.txt' INTO TABLE v1;
+
+--echo # Testing multiple-table statements
+
+--error ER_ACCESS_DENIED_ERROR
+CREATE TABLE t2 AS SELECT fname, ftype FROM v1;
+CREATE TABLE t2 (fname VARCHAR(256) NOT NULL, ftype CHAR(4) NOT NULL) ENGINE=MyISAM;
+INSERT INTO t2 VALUES ('test','bin');
+--error ER_ACCESS_DENIED_ERROR
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+--error ER_ACCESS_DENIED_ERROR
+INSERT IGNORE INTO t2 SELECT fname, ftype FROM v1;
+--error ER_ACCESS_DENIED_ERROR
+REPLACE INTO t2 SELECT fname, ftype FROM v1;
+ALTER TABLE t2 ADD cnt INT NOT NULL;
+--error ER_ACCESS_DENIED_ERROR
+UPDATE t2,v1 SET t2.cnt=1 WHERE t2.fname=v1.fname;
+--error ER_ACCESS_DENIED_ERROR
+DELETE t2 FROM t2,v1 WHERE t2.fname=v1.fname AND t2.ftype=v1.ftype;
+DROP TABLE t2;
+--echo #
+--echo # End of tests with SQL SECURITY INVOKER
+--echo #
+
+
 --disconnect user
 --connection default
 SELECT user();

=== modified file 'storage/connect/mysql-test/connect/t/mysql_grant.test'
--- storage/connect/mysql-test/connect/t/mysql_grant.test	2013-10-27 09:37:12 +0000
+++ storage/connect/mysql-test/connect/t/mysql_grant.test	2015-02-17 13:40:21 +0000
@@ -54,7 +54,7 @@ CREATE VIEW v1 AS SELECT * FROM t1;
 --echo # Testing a VIEW created with FILE privileges but accessed with no FILE
 --connection default
 SELECT user();
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 --connection user
 SELECT user();
 --error ER_ACCESS_DENIED_ERROR

=== modified file 'storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test'
--- storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test	2013-05-24 11:21:06 +0000
+++ storage/connect/mysql-test/connect/t/odbc_sqlite3_grant.test	2015-02-24 10:30:03 +0000
@@ -56,9 +56,10 @@ ALTER TABLE t1 READONLY=1;
 CREATE VIEW v1 AS SELECT * FROM t1;
 
 --echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--echo # using SQL SECIRITY INVOKER
 --connection default
 SELECT user();
-CREATE VIEW v1 AS SELECT * FROM t1;
+CREATE SQL SECURITY INVOKER VIEW v1 AS SELECT * FROM t1;
 --connection user
 SELECT user();
 --error ER_ACCESS_DENIED_ERROR
@@ -70,6 +71,17 @@ UPDATE v1 SET a=123;
 --error ER_ACCESS_DENIED_ERROR
 DELETE FROM v1;
 
+--echo # Testing a VIEW created with FILE privileges but accessed with no FILE
+--echo # using SQL SECIRITY DEFINER
+--connection default
+DROP VIEW v1;
+SELECT user();
+CREATE SQL SECURITY DEFINER VIEW v1 AS SELECT * FROM t1;
+--connection user
+SELECT user();
+SELECT * FROM v1 WHERE a='test1';
+
+
 --disconnect user
 --connection default
 SELECT user();


Follow ups

References