mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-24557 mariadb-dump: import MySQL 5.7+ users, grants, roles
mariadb-dump --system=users/all dumps a MySQL 5.7+ source as MariaDB statements. - MySQL privileges are mapped; unmappable ones are dropped with a "# WARNING". - CREATE USER clauses MariaDB lacks are dropped. An unsupported auth plugin locks the account. - Roles are inferred from mysql.user. Same-named roles on several hosts merge. For a MariaDB source, the /*!80001 role statements are gone, DROP USER IF EXISTS moved to /*M!100103, and the roles path sets NO_AUTO_CREATE_USER while grants run. Tests replay a dump captured from MySQL 26.7.0; recapture it after changing the MySQL-source code (recipe: mysql8_users_setup.sql) Assisted-By: Claude:claude-5.5-sonnet
This commit is contained in:
@@ -53,8 +53,12 @@ SET_TARGET_PROPERTIES(mariadb-test PROPERTIES ENABLE_EXPORTS TRUE)
|
||||
MYSQL_ADD_EXECUTABLE(mariadb-check mysqlcheck.c)
|
||||
TARGET_LINK_LIBRARIES(mariadb-check ${CLIENT_LIB})
|
||||
|
||||
ADD_LIBRARY(mysql8_translate STATIC mysql8_translate.cc mysql8_translate.h)
|
||||
TARGET_LINK_LIBRARIES(mysql8_translate PRIVATE mysys strings dbug)
|
||||
TARGET_INCLUDE_DIRECTORIES(mysql8_translate PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
MYSQL_ADD_EXECUTABLE(mariadb-dump mysqldump.cc ../sql-common/my_user.c connection_pool.cc)
|
||||
TARGET_LINK_LIBRARIES(mariadb-dump ${CLIENT_LIB})
|
||||
TARGET_LINK_LIBRARIES(mariadb-dump ${CLIENT_LIB} mysql8_translate)
|
||||
|
||||
ADD_LIBRARY(import_util STATIC import_util.cc import_util.h)
|
||||
IF (PCRE2_DEBIAN_HACK)
|
||||
|
||||
709
client/mysql8_translate.cc
Normal file
709
client/mysql8_translate.cc
Normal file
@@ -0,0 +1,709 @@
|
||||
/*
|
||||
Copyright (c) 2026, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
||||
*/
|
||||
|
||||
#include "mysql8_translate.h"
|
||||
#include <my_sys.h>
|
||||
#include <m_string.h>
|
||||
|
||||
/* Die if a DYNAMIC_STRING call failed to allocate. */
|
||||
static void check_alloc(bool failed)
|
||||
{
|
||||
if (failed)
|
||||
dump_fatal("Couldn't allocate memory");
|
||||
}
|
||||
|
||||
static void append(DYNAMIC_STRING *ds, const char *str)
|
||||
{
|
||||
check_alloc(dynstr_append(ds, str));
|
||||
}
|
||||
|
||||
static void append_mem(DYNAMIC_STRING *ds, const char *str, size_t len)
|
||||
{
|
||||
check_alloc(dynstr_append_mem(ds, str, uint(len)));
|
||||
}
|
||||
|
||||
/*
|
||||
Privileges that need translating. Other dynamic privileges (see
|
||||
is_dynamic_priv_name()) are dropped with a # WARNING; other names pass
|
||||
through.
|
||||
|
||||
mariadb_priv: the MariaDB privileges to emit; NULL in the first slot means
|
||||
no equivalent, drop with a # WARNING. CREATE ROLE and DROP ROLE have no '_',
|
||||
so they are listed to be dropped.
|
||||
|
||||
ALLOW_NONEXISTENT_DEFINER and SERVICE_CONNECTION_ADMIN are not mapped: the
|
||||
nearest MariaDB privileges (SET USER, CONNECTION ADMIN) grant more.
|
||||
*/
|
||||
struct Mysql8_priv_map_entry
|
||||
{
|
||||
const char *mysql_priv;
|
||||
size_t len;
|
||||
const char *mariadb_priv[2];
|
||||
};
|
||||
static const struct Mysql8_priv_map_entry mysql8_priv_map[]=
|
||||
{
|
||||
{STRING_WITH_LEN("BINLOG_ADMIN"), {"BINLOG REPLAY", "BINLOG ADMIN"}},
|
||||
{STRING_WITH_LEN("CONNECTION_ADMIN"), {"CONNECTION ADMIN", NULL}},
|
||||
{STRING_WITH_LEN("CREATE ROLE"), {NULL, NULL}},
|
||||
{STRING_WITH_LEN("DROP ROLE"), {NULL, NULL}},
|
||||
{STRING_WITH_LEN("REPLICATION_APPLIER"), {"BINLOG REPLAY", NULL}},
|
||||
{STRING_WITH_LEN("REPLICATION_SLAVE_ADMIN"),
|
||||
{"REPLICATION SLAVE ADMIN", NULL}},
|
||||
{STRING_WITH_LEN("SET_ANY_DEFINER"), {"SET USER", NULL}},
|
||||
{STRING_WITH_LEN("SHOW_ROUTINE"), {"SHOW CREATE ROUTINE", NULL}}
|
||||
};
|
||||
|
||||
/*
|
||||
Skip the quoted token at *p (' " or `): doubled quotes, and backslash
|
||||
escapes except in backticks. Returns the position after the closing quote,
|
||||
or the end of string if unterminated.
|
||||
*/
|
||||
static const char *skip_quoted(const char *p)
|
||||
{
|
||||
char q= *p++;
|
||||
while (*p)
|
||||
{
|
||||
if (*p == '\\' && p[1] && q != '`')
|
||||
p+= 2;
|
||||
else if (*p == q)
|
||||
{
|
||||
if (p[1] == q)
|
||||
p+= 2;
|
||||
else
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
p++;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
What to do with a CREATE USER clause (see user_clauses): keep, drop, drop
|
||||
with a warning unless the value is DEFAULT or 0 (CL_DROP_WARN: password
|
||||
policy, CL_DROP_WARN_OPT: other options), or the authentication (CL_AUTH)
|
||||
or account lock (CL_ACCOUNT) clause.
|
||||
*/
|
||||
enum Clause_action { CL_KEEP, CL_DROP, CL_DROP_WARN, CL_DROP_WARN_OPT, CL_AUTH,
|
||||
CL_ACCOUNT };
|
||||
|
||||
/* A keyword for scan_top_level(); it must begin with a space. */
|
||||
struct Kw_entry
|
||||
{
|
||||
LEX_CSTRING kw;
|
||||
enum Clause_action action;
|
||||
};
|
||||
|
||||
/*
|
||||
Find the first top-level keyword of `kws` ({NULL,0}-terminated) in `start`,
|
||||
skipping quoted and parenthesised regions. The longest keyword wins at a
|
||||
position. Sets *idx to its index; NULL if none match. Resume past a hit at
|
||||
pos + kws[*idx].kw.length.
|
||||
*/
|
||||
static const char *scan_top_level(const char *start,
|
||||
const struct Kw_entry *kws, int *idx)
|
||||
{
|
||||
int depth= 0;
|
||||
for (const char *p= start; *p; )
|
||||
{
|
||||
if (*p == '\'' || *p == '"' || *p == '`') { p= skip_quoted(p); continue; }
|
||||
if (*p == '(') { depth++; p++; continue; }
|
||||
if (*p == ')') { depth--; p++; continue; }
|
||||
if (depth == 0 && *p == ' ')
|
||||
{
|
||||
int best= -1;
|
||||
size_t best_len= 0;
|
||||
for (int i= 0; kws[i].kw.str; i++)
|
||||
{
|
||||
DBUG_ASSERT(kws[i].kw.str[0] == ' ');
|
||||
if (kws[i].kw.length > best_len &&
|
||||
strncmp(p, kws[i].kw.str, kws[i].kw.length) == 0)
|
||||
{ best= i; best_len= kws[i].kw.length; }
|
||||
}
|
||||
if (best >= 0) { *idx= best; return p; }
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* scan_top_level() for a single keyword. */
|
||||
static const char *scan_keyword(const char *start, const LEX_CSTRING *kw)
|
||||
{
|
||||
const struct Kw_entry kws[]= {{*kw, CL_KEEP}, {{NULL, 0}, CL_KEEP}};
|
||||
int idx;
|
||||
return scan_top_level(start, kws, &idx);
|
||||
}
|
||||
|
||||
static const struct Mysql8_priv_map_entry *
|
||||
lookup_mysql8_priv(const char *name, size_t len)
|
||||
{
|
||||
for (size_t i= 0; i < array_elements(mysql8_priv_map); i++)
|
||||
if (mysql8_priv_map[i].len == len &&
|
||||
memcmp(mysql8_priv_map[i].mysql_priv, name, len) == 0)
|
||||
return &mysql8_priv_map[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Trim spaces and tabs around [s, e); return the length. */
|
||||
static size_t trim_priv(const char **s, const char *e)
|
||||
{
|
||||
while (*s < e && (**s == ' ' || **s == '\t')) (*s)++;
|
||||
while (e > *s && (e[-1] == ' ' || e[-1] == '\t')) e--;
|
||||
return size_t(e - *s);
|
||||
}
|
||||
|
||||
|
||||
/* The translated privilege list of one GRANT. */
|
||||
struct Priv_state
|
||||
{
|
||||
DYNAMIC_STRING privs;
|
||||
/* Mapped privileges already in `privs`: several MySQL ones map to the same
|
||||
(BINLOG_ADMIN and REPLICATION_APPLIER both give BINLOG REPLAY). */
|
||||
const char *mapped[2 * array_elements(mysql8_priv_map)];
|
||||
uint n_mapped;
|
||||
FILE *out; /* "# WARNING" lines go here */
|
||||
};
|
||||
|
||||
static void append_priv(struct Priv_state *st, const char *tok, size_t len)
|
||||
{
|
||||
if (st->privs.length)
|
||||
append(&st->privs, ", ");
|
||||
append_mem(&st->privs, tok, len);
|
||||
}
|
||||
|
||||
static void append_mapped_priv(struct Priv_state *st, const char *tok)
|
||||
{
|
||||
for (uint i= 0; i < st->n_mapped; i++)
|
||||
if (strcmp(st->mapped[i], tok) == 0)
|
||||
return;
|
||||
DBUG_ASSERT(st->n_mapped < array_elements(st->mapped));
|
||||
st->mapped[st->n_mapped++]= tok;
|
||||
append_priv(st, tok, strlen(tok));
|
||||
}
|
||||
|
||||
|
||||
/* MySQL dynamic privilege names are [A-Z0-9_]+ with a '_' (BACKUP_ADMIN). */
|
||||
static bool is_dynamic_priv_name(const char *s, size_t len)
|
||||
{
|
||||
bool underscore= false;
|
||||
for (size_t i= 0; i < len; i++)
|
||||
{
|
||||
if (s[i] == '_')
|
||||
underscore= true;
|
||||
else if (!((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= '0' && s[i] <= '9')))
|
||||
return false;
|
||||
}
|
||||
return underscore;
|
||||
}
|
||||
|
||||
|
||||
/* Add privilege [start, end) to st; true if it was mapped or dropped. */
|
||||
static bool process_priv(const char *start, const char *end,
|
||||
struct Priv_state *st)
|
||||
{
|
||||
size_t plen= trim_priv(&start, end);
|
||||
if (!plen)
|
||||
return false;
|
||||
|
||||
const struct Mysql8_priv_map_entry *e= lookup_mysql8_priv(start, plen);
|
||||
if (!e && !is_dynamic_priv_name(start, plen))
|
||||
{
|
||||
append_priv(st, start, plen);
|
||||
return false;
|
||||
}
|
||||
if (!e || !e->mariadb_priv[0])
|
||||
{
|
||||
dump_emit_comment_warning(st->out, "WARNING: dropped MySQL privilege "
|
||||
"%.*s (no MariaDB equivalent)",
|
||||
int(plen), start);
|
||||
return true;
|
||||
}
|
||||
for (uint i= 0; i < array_elements(e->mariadb_priv); i++)
|
||||
if (e->mariadb_priv[i])
|
||||
append_mapped_priv(st, e->mariadb_priv[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Split [start, end) at top-level commas and process_priv() each part. */
|
||||
static bool translate_priv_list(const char *start, const char *end,
|
||||
struct Priv_state *st)
|
||||
{
|
||||
bool changed= false;
|
||||
const char *p= start;
|
||||
const char *priv_start= start;
|
||||
int depth= 0;
|
||||
while (p < end)
|
||||
{
|
||||
if (*p == '\'' || *p == '"' || *p == '`')
|
||||
{
|
||||
p= skip_quoted(p);
|
||||
continue;
|
||||
}
|
||||
if (*p == '(') { depth++; p++; continue; }
|
||||
if (*p == ')') { depth--; p++; continue; }
|
||||
if (depth == 0 && *p == ',')
|
||||
{
|
||||
changed|= process_priv(priv_start, p, st);
|
||||
priv_start= p + 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
changed|= process_priv(priv_start, end, st);
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
True if s contains star-slash, which would end an enclosing version comment
|
||||
and run the rest as SQL; neither QUOTE() nor backticks escape it.
|
||||
*/
|
||||
static bool has_comment_close(const char *s)
|
||||
{
|
||||
return strstr(s, "*/") != NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Print s with each star-slash split over two literals, '..*', '/..'; the
|
||||
caller wraps the result in CONCAT(). With `escape`, s is the inside of a
|
||||
literal and its quotes and backslashes are doubled; else s is a whole
|
||||
literal, printed as is.
|
||||
*/
|
||||
static void put_split(FILE *out, const char *s, bool escape)
|
||||
{
|
||||
for (; *s; s++)
|
||||
{
|
||||
if (s[0] == '*' && s[1] == '/')
|
||||
{
|
||||
fputs("*', '/", out);
|
||||
s++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (escape && (*s == '\'' || *s == '\\'))
|
||||
fputc(*s, out);
|
||||
fputc(*s, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void dump_put_comment_safe_literal(FILE *out, const char *literal)
|
||||
{
|
||||
DBUG_ASSERT(literal[0] == '\'');
|
||||
if (has_comment_close(literal))
|
||||
{
|
||||
fputs("CONCAT(", out);
|
||||
put_split(out, literal, false);
|
||||
fputc(')', out);
|
||||
}
|
||||
else
|
||||
fputs(literal, out);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Format fmt/args into stackbuf, or into a my_malloc'ed buffer if it does not
|
||||
fit. Returns the string; the caller my_free()s it unless it is stackbuf.
|
||||
Dies on a formatting error or OOM. args is traversed via va_copy.
|
||||
*/
|
||||
static char *vformat_dup(const char *fmt, va_list args,
|
||||
char *stackbuf, size_t stacksz)
|
||||
{
|
||||
va_list copy;
|
||||
int len;
|
||||
|
||||
va_copy(copy, args);
|
||||
len= vsnprintf(stackbuf, stacksz, fmt, copy);
|
||||
va_end(copy);
|
||||
if (len < 0)
|
||||
dump_fatal("vsnprintf failed formatting dump output");
|
||||
if (size_t(len) < stacksz)
|
||||
return stackbuf;
|
||||
|
||||
char *heap= static_cast<char*>(my_malloc(PSI_NOT_INSTRUMENTED,
|
||||
size_t(len) + 1, MYF(MY_WME)));
|
||||
if (!heap)
|
||||
dump_fatal("Couldn't allocate memory for dump output");
|
||||
va_copy(copy, args);
|
||||
vsnprintf(heap, size_t(len) + 1, fmt, copy);
|
||||
va_end(copy);
|
||||
return heap;
|
||||
}
|
||||
|
||||
|
||||
/* Fold CR/LF to a space, keeping a "#" comment on one line. */
|
||||
static void fold_eol(char *s)
|
||||
{
|
||||
for (; *s; s++)
|
||||
if (*s == '\n' || *s == '\r')
|
||||
*s= ' ';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Append a "# ..." line to `out`. CR/LF are folded, as QUOTE() and backticks
|
||||
do not escape them and the rest would run as SQL. The "#" counterpart of
|
||||
the star-slash split in dump_emit_versioned_wrap().
|
||||
*/
|
||||
static void vformat_comment_line(DYNAMIC_STRING *out, const char *fmt,
|
||||
va_list args)
|
||||
{
|
||||
char stackbuf[1024], *body;
|
||||
|
||||
body= vformat_dup(fmt, args, stackbuf, sizeof(stackbuf));
|
||||
fold_eol(body);
|
||||
append(out, "# ");
|
||||
append(out, body);
|
||||
append(out, "\n");
|
||||
if (body != stackbuf)
|
||||
my_free(body);
|
||||
}
|
||||
|
||||
|
||||
static void vemit_comment_warning(FILE *out, const char *fmt, va_list args)
|
||||
{
|
||||
DYNAMIC_STRING line;
|
||||
|
||||
check_alloc(init_dynamic_string(&line, "", 256, 256));
|
||||
vformat_comment_line(&line, fmt, args);
|
||||
fputs(line.str, out);
|
||||
dynstr_free(&line);
|
||||
}
|
||||
|
||||
|
||||
void dump_emit_comment_warning(FILE *out, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vemit_comment_warning(out, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Emit prefix, the formatted body, comment end and ';'. Pass user data via
|
||||
%s, not in fmt, so a '%' in an identifier is not a format specifier. A body
|
||||
with star-slash is emitted as EXECUTE IMMEDIATE CONCAT('..*', '/..'), which
|
||||
needs M!100203 whatever the prefix.
|
||||
*/
|
||||
static void vemit_versioned_wrap(FILE *out, const char *prefix,
|
||||
const char *fmt, va_list args)
|
||||
{
|
||||
char stackbuf[1024], *body;
|
||||
|
||||
body= vformat_dup(fmt, args, stackbuf, sizeof(stackbuf));
|
||||
if (has_comment_close(body))
|
||||
{
|
||||
fputs("/*M!100203 EXECUTE IMMEDIATE CONCAT('", out);
|
||||
put_split(out, body, true);
|
||||
fputs("') */;\n", out);
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs(prefix, out);
|
||||
fputs(body, out);
|
||||
fputs(" */;\n", out);
|
||||
}
|
||||
if (body != stackbuf)
|
||||
my_free(body);
|
||||
}
|
||||
|
||||
|
||||
void dump_emit_versioned_wrap(FILE *out, const char *prefix,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vemit_versioned_wrap(out, prefix, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Translate and emit a MySQL 5.7+ GRANT. Returns false if it is portable and
|
||||
the caller should print it as is. A pure role grant emits nothing:
|
||||
dump_role_hierarchy_mysql() covers it.
|
||||
Output: a "# WARNING" per dropped privilege, and for a dropped AS clause
|
||||
and grant option (only if a privilege survives), then the GRANT, if any
|
||||
privilege survived.
|
||||
|
||||
role_name: the QUOTE()'d name of a role grantee, else NULL. A MariaDB role
|
||||
has no host, so it replaces `role`@`host`.
|
||||
*/
|
||||
bool translate_mysql8_grant(const char *grant, FILE *out, const char *role_name)
|
||||
{
|
||||
DBUG_ASSERT(strncmp(grant, "GRANT ", 6) == 0);
|
||||
static const LEX_CSTRING kw_on= {STRING_WITH_LEN(" ON ")};
|
||||
static const LEX_CSTRING kw_as= {STRING_WITH_LEN(" AS ")};
|
||||
static const LEX_CSTRING kw_to= {STRING_WITH_LEN(" TO ")};
|
||||
static const LEX_CSTRING grant_opt= {STRING_WITH_LEN(" WITH GRANT OPTION")};
|
||||
|
||||
const char *on_ptr= scan_keyword(grant, &kw_on);
|
||||
if (!on_ptr)
|
||||
{
|
||||
/* Pure role grant, see the function header. */
|
||||
return true;
|
||||
}
|
||||
const char *as_ptr= scan_keyword(on_ptr, &kw_as);
|
||||
|
||||
struct Priv_state st;
|
||||
check_alloc(init_dynamic_string(&st.privs, "", 256, 1024));
|
||||
st.n_mapped= 0;
|
||||
st.out= out;
|
||||
|
||||
bool changed= translate_priv_list(grant + sizeof("GRANT ") - 1, on_ptr, &st);
|
||||
if (!changed && !as_ptr && !role_name)
|
||||
{
|
||||
dynstr_free(&st.privs);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st.privs.length)
|
||||
{
|
||||
if (as_ptr)
|
||||
dump_emit_comment_warning(out,
|
||||
"WARNING: partial-revokes AS clause dropped (no MariaDB "
|
||||
"equivalent): %s; the grant below is wider than on the MySQL source",
|
||||
as_ptr + 1);
|
||||
const char *tail_end= as_ptr ? as_ptr : grant + strlen(grant);
|
||||
bool with_opt= size_t(tail_end - on_ptr) >= grant_opt.length &&
|
||||
memcmp(tail_end - grant_opt.length, grant_opt.str,
|
||||
grant_opt.length) == 0;
|
||||
/*
|
||||
MySQL keeps the grant option per dynamic privilege, MariaDB per level:
|
||||
keeping it would make every global privilege grantable. Drop it when a
|
||||
mapped privilege is emitted; MySQL prints dynamic privileges on a GRANT
|
||||
of their own, so no static privilege loses it.
|
||||
*/
|
||||
if (st.n_mapped && with_opt)
|
||||
{
|
||||
dump_emit_comment_warning(out,
|
||||
"WARNING: WITH GRANT OPTION dropped from a translated privilege: "
|
||||
"MariaDB would make all global privileges of the account grantable");
|
||||
tail_end-= grant_opt.length;
|
||||
with_opt= false;
|
||||
}
|
||||
const char *to_ptr= role_name ? scan_keyword(on_ptr, &kw_to) : NULL;
|
||||
if (to_ptr && to_ptr < tail_end)
|
||||
dump_emit_versioned_wrap(out, "/*M!100005 ", "GRANT %.*s%.*s TO %s%s",
|
||||
int(st.privs.length), st.privs.str,
|
||||
int(to_ptr - on_ptr), on_ptr, role_name,
|
||||
with_opt ? grant_opt.str : "");
|
||||
else
|
||||
dump_emit_versioned_wrap(out, "/*M!100005 ", "GRANT %.*s%.*s",
|
||||
int(st.privs.length), st.privs.str,
|
||||
int(tail_end - on_ptr), on_ptr);
|
||||
}
|
||||
dynstr_free(&st.privs);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* SHOW GRANTS emits REVOKE only for partial_revokes, which MariaDB lacks. */
|
||||
void translate_mysql8_revoke(const char *revoke, FILE *out)
|
||||
{
|
||||
dump_emit_comment_warning(out,
|
||||
"WARNING: partial REVOKE has no MariaDB equivalent, ignored: %s", revoke);
|
||||
dump_emit_comment_warning(out,
|
||||
" the wider GRANT remains in effect; re-grant at the narrower "
|
||||
"scope if needed.");
|
||||
}
|
||||
|
||||
|
||||
/* SHOW CREATE USER clauses. A clause runs to the next, taking its value. */
|
||||
static const struct Kw_entry user_clauses[]=
|
||||
{
|
||||
{{STRING_WITH_LEN(" IDENTIFIED ")}, CL_AUTH},
|
||||
{{STRING_WITH_LEN(" AND IDENTIFIED ")}, CL_DROP},
|
||||
{{STRING_WITH_LEN(" INITIAL AUTHENTICATION ")}, CL_DROP},
|
||||
{{STRING_WITH_LEN(" DEFAULT ROLE ")}, CL_DROP},
|
||||
{{STRING_WITH_LEN(" REQUIRE ")}, CL_KEEP},
|
||||
{{STRING_WITH_LEN(" WITH MAX_")}, CL_KEEP},
|
||||
{{STRING_WITH_LEN(" PASSWORD EXPIRE")}, CL_KEEP},
|
||||
{{STRING_WITH_LEN(" PASSWORD HISTORY ")}, CL_DROP_WARN},
|
||||
{{STRING_WITH_LEN(" PASSWORD REUSE INTERVAL ")}, CL_DROP_WARN},
|
||||
/* No trailing space: the bare form can end the string, and " REQUIRE "
|
||||
would match instead. */
|
||||
{{STRING_WITH_LEN(" PASSWORD REQUIRE CURRENT")}, CL_DROP_WARN},
|
||||
{{STRING_WITH_LEN(" FAILED_LOGIN_ATTEMPTS ")}, CL_DROP_WARN_OPT},
|
||||
{{STRING_WITH_LEN(" PASSWORD_LOCK_TIME ")}, CL_DROP_WARN_OPT},
|
||||
{{STRING_WITH_LEN(" COMMENT ")}, CL_DROP_WARN_OPT},
|
||||
{{STRING_WITH_LEN(" ATTRIBUTE ")}, CL_DROP_WARN_OPT},
|
||||
{{STRING_WITH_LEN(" ACCOUNT UNLOCK")}, CL_ACCOUNT},
|
||||
{{STRING_WITH_LEN(" ACCOUNT LOCK")}, CL_ACCOUNT},
|
||||
{{NULL, 0}, CL_KEEP}
|
||||
};
|
||||
|
||||
/*
|
||||
Auth plugins MariaDB can use on import: mysql_native_password and
|
||||
caching_sha2_password (needs the auth_mysql_sha2 plugin, else the statement
|
||||
fails). Any other leading plugin gets a warning, loses its IDENTIFIED
|
||||
clause and gets ACCOUNT LOCK, see translate_mysql8_create_user().
|
||||
*/
|
||||
static const char *const mariadb_known_auth_plugins[]= {
|
||||
"mysql_native_password",
|
||||
"caching_sha2_password",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
/* Queue a "# ..." line, to be printed after its statement. */
|
||||
static void queue_warning(DYNAMIC_STRING *warnings, const char *fmt, ...)
|
||||
ATTRIBUTE_FORMAT(printf, 2, 3);
|
||||
static void queue_warning(DYNAMIC_STRING *warnings, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vformat_comment_line(warnings, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Queue a warning for the first IDENTIFIED WITH plugin if it is not in
|
||||
mariadb_known_auth_plugins, and for each further factor (dropped, MariaDB
|
||||
has no second factor). Returns true if the first plugin is unsupported.
|
||||
*/
|
||||
static bool scan_identified_plugins(const char *user_str,
|
||||
DYNAMIC_STRING *warnings_out)
|
||||
{
|
||||
static const LEX_CSTRING kw_idw= {STRING_WITH_LEN(" IDENTIFIED WITH '")};
|
||||
bool first_unsupported= false;
|
||||
bool first= true;
|
||||
|
||||
for (const char *p= user_str;
|
||||
(p= scan_keyword(p, &kw_idw)) != NULL; )
|
||||
{
|
||||
const char *plugin_start= p + kw_idw.length;
|
||||
const char *plugin_end= strchr(plugin_start, '\'');
|
||||
if (!plugin_end)
|
||||
break; /* malformed: unterminated plugin name */
|
||||
size_t plen= plugin_end - plugin_start;
|
||||
|
||||
/* Only the first plugin decides whether IDENTIFIED is dropped. */
|
||||
bool supported= false;
|
||||
for (const char *const *m= mariadb_known_auth_plugins; *m; m++)
|
||||
{
|
||||
if (strlen(*m) == plen && strncmp(plugin_start, *m, plen) == 0)
|
||||
{
|
||||
supported= true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!first)
|
||||
queue_warning(warnings_out,
|
||||
"WARNING: additional authentication factor '%.*s' "
|
||||
"dropped: MariaDB cannot require it", int(plen),
|
||||
plugin_start);
|
||||
else if (!supported)
|
||||
{
|
||||
queue_warning(warnings_out,
|
||||
"WARNING: auth plugin '%.*s' not shipped with "
|
||||
"MariaDB; user created with no password and ACCOUNT LOCK - "
|
||||
"set a password and ACCOUNT UNLOCK before login",
|
||||
int(plen), plugin_start);
|
||||
first_unsupported= true;
|
||||
}
|
||||
first= false;
|
||||
p= plugin_end + 1;
|
||||
}
|
||||
return first_unsupported;
|
||||
}
|
||||
|
||||
/*
|
||||
Emit CREATE USER from MySQL 5.7+: drop clauses MariaDB lacks, add ACCOUNT
|
||||
LOCK when the authentication is dropped, then the queued # WARNING lines.
|
||||
*/
|
||||
void translate_mysql8_create_user(const char *user_str, FILE *out,
|
||||
bool replace_into, bool ignore)
|
||||
{
|
||||
DYNAMIC_STRING warnings;
|
||||
check_alloc(init_dynamic_string(&warnings, "", 256, 256));
|
||||
bool ident_incompat= scan_identified_plugins(user_str, &warnings);
|
||||
|
||||
fputs("CREATE ", out);
|
||||
if (replace_into)
|
||||
fputs("/*M!100103 OR REPLACE */ ", out);
|
||||
fputs("USER", out);
|
||||
if (ignore)
|
||||
fputs(" IF NOT EXISTS", out);
|
||||
|
||||
/* The text before the first clause (" `u`@`h`") is verbatim. */
|
||||
const char *cur= user_str;
|
||||
bool account_emitted= false;
|
||||
int idx;
|
||||
const char *clause= scan_top_level(user_str, user_clauses, &idx);
|
||||
while (clause)
|
||||
{
|
||||
int nidx= 0;
|
||||
const char *next= scan_top_level(clause + user_clauses[idx].kw.length,
|
||||
user_clauses, &nidx);
|
||||
const char *clause_end= next ? next : user_str + strlen(user_str);
|
||||
|
||||
fwrite(cur, 1, clause - cur, out);
|
||||
|
||||
switch (user_clauses[idx].action) {
|
||||
case CL_DROP:
|
||||
break;
|
||||
case CL_DROP_WARN:
|
||||
case CL_DROP_WARN_OPT:
|
||||
{
|
||||
/* The value follows the keyword; DEFAULT and 0 mean no policy. */
|
||||
const char *val= clause + user_clauses[idx].kw.length;
|
||||
while (val < clause_end && *val == ' ')
|
||||
val++;
|
||||
bool no_policy= !strncmp(val, "DEFAULT", 7) ||
|
||||
(*val == '0' && !my_isdigit(&my_charset_latin1, val[1]));
|
||||
if (!no_policy)
|
||||
queue_warning(&warnings,
|
||||
user_clauses[idx].action == CL_DROP_WARN ?
|
||||
"WARNING: password policy clause dropped (no MariaDB "
|
||||
"equivalent):%.*s" :
|
||||
"WARNING: account option dropped (no MariaDB "
|
||||
"equivalent):%.*s", int(clause_end - clause), clause);
|
||||
break;
|
||||
}
|
||||
case CL_AUTH:
|
||||
if (!ident_incompat)
|
||||
fwrite(clause, 1, clause_end - clause, out);
|
||||
break;
|
||||
case CL_ACCOUNT:
|
||||
account_emitted= true;
|
||||
if (ident_incompat)
|
||||
fputs(" ACCOUNT LOCK", out);
|
||||
else
|
||||
fwrite(clause, 1, clause_end - clause, out);
|
||||
break;
|
||||
case CL_KEEP:
|
||||
fwrite(clause, 1, clause_end - clause, out);
|
||||
break;
|
||||
}
|
||||
|
||||
cur= clause_end;
|
||||
clause= next;
|
||||
idx= nidx;
|
||||
}
|
||||
fputs(cur, out);
|
||||
if (ident_incompat && !account_emitted)
|
||||
fputs(" ACCOUNT LOCK", out);
|
||||
fputs(";\n", out);
|
||||
fputs(warnings.str, out);
|
||||
dynstr_free(&warnings);
|
||||
}
|
||||
61
client/mysql8_translate.h
Normal file
61
client/mysql8_translate.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Copyright (c) 2026, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
||||
*/
|
||||
|
||||
#ifndef MYSQL8_TRANSLATE_INCLUDED
|
||||
#define MYSQL8_TRANSLATE_INCLUDED
|
||||
|
||||
#include <my_global.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Dump output helpers, for MariaDB and MySQL sources alike. */
|
||||
|
||||
/* Fatal error (out of memory, formatting failure); defined by the host. */
|
||||
[[noreturn]] void dump_fatal(const char *msg);
|
||||
|
||||
/*
|
||||
Print a quoted literal inside a version comment, as CONCAT('..*', '/..')
|
||||
if it contains a comment terminator.
|
||||
*/
|
||||
void dump_put_comment_safe_literal(FILE *out, const char *literal);
|
||||
|
||||
/* One "# " comment line; CR/LF are folded. */
|
||||
void dump_emit_comment_warning(FILE *out, const char *fmt, ...)
|
||||
ATTRIBUTE_FORMAT(printf, 2, 3);
|
||||
|
||||
/*
|
||||
Version comment: prefix, formatted body, comment end and ';'.
|
||||
A body with a comment terminator goes through EXECUTE IMMEDIATE CONCAT().
|
||||
*/
|
||||
void dump_emit_versioned_wrap(FILE *out, const char *prefix,
|
||||
const char *fmt, ...)
|
||||
ATTRIBUTE_FORMAT(printf, 3, 4);
|
||||
|
||||
/* Translation of MySQL 5.7+ SHOW GRANTS / SHOW CREATE USER output. */
|
||||
|
||||
/*
|
||||
Returns true if handled (the caller must not print the GRANT).
|
||||
role_name, if not NULL, is the quoted role name replacing `role`@`host`.
|
||||
*/
|
||||
bool translate_mysql8_grant(const char *grant, FILE *out,
|
||||
const char *role_name);
|
||||
void translate_mysql8_revoke(const char *revoke, FILE *out);
|
||||
|
||||
/* user_str is the SHOW CREATE USER text after "CREATE USER", with its space. */
|
||||
void translate_mysql8_create_user(const char *user_str, FILE *out,
|
||||
bool replace_into, bool ignore);
|
||||
|
||||
#endif
|
||||
@@ -61,6 +61,7 @@
|
||||
|
||||
#include <welcome_copyright_notice.h> /* ORACLE_WELCOME_COPYRIGHT_NOTICE */
|
||||
#include "connection_pool.h"
|
||||
#include "mysql8_translate.h"
|
||||
/* Exit codes */
|
||||
|
||||
#define EX_USAGE 1
|
||||
@@ -1448,6 +1449,13 @@ static int get_options(int *argc, char ***argv)
|
||||
return(EX_USAGE);
|
||||
}
|
||||
}
|
||||
if (opt_replace_into && opt_ignore)
|
||||
{
|
||||
/* REPLACE IGNORE and OR REPLACE ... IF NOT EXISTS are not valid SQL. */
|
||||
fprintf(stderr, "%s: Warning: --replace is ignored, "
|
||||
"--insert-ignore takes precedence.\n", my_progname_short);
|
||||
opt_replace_into= 0;
|
||||
}
|
||||
if (opt_asof_timestamp && strchr(opt_asof_timestamp, '\''))
|
||||
{
|
||||
fprintf(stderr, "%s: Incorrect DATETIME value: '%s'\n",
|
||||
@@ -1517,6 +1525,14 @@ static void die(int error_num, const char* fmt_reason, ...)
|
||||
}
|
||||
|
||||
|
||||
/* Fatal-error hook of the translators; die() restarts a stopped slave. */
|
||||
void dump_fatal(const char *msg)
|
||||
{
|
||||
die(EX_EOM, "%s", msg);
|
||||
exit(EX_EOM); /* die() is not declared noreturn */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Prints out an error message and maybe kills the process.
|
||||
|
||||
@@ -4588,12 +4604,97 @@ static char *getTableName(int reset, int want_sequences)
|
||||
|
||||
|
||||
/*
|
||||
dump user/role grants
|
||||
ARGS
|
||||
user_role: is either a user, or a role
|
||||
True if mysql.<table> is visible to the dumping user. A failed probe is
|
||||
fatal even under --force: treating the table as absent would silently
|
||||
drop the roles from the dump.
|
||||
*/
|
||||
static bool mysql_table_exists(const char *table)
|
||||
{
|
||||
MYSQL_RES *res;
|
||||
char query[200];
|
||||
bool found= false;
|
||||
|
||||
static int dump_grants(const char *user_role)
|
||||
my_snprintf(query, sizeof(query),
|
||||
"SELECT 1 FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA='mysql' AND TABLE_NAME='%s'", table);
|
||||
if (mysql_query_with_error_report(mysql, &res, query))
|
||||
dump_fatal("Couldn't tell which mysql tables the source has");
|
||||
found= mysql_num_rows(res) > 0;
|
||||
mysql_free_result(res);
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
What the source is. The version number cannot tell MySQL from MariaDB
|
||||
(MariaDB 10+ is >= 80000, MySQL 26.x is above MariaDB 13.x, and
|
||||
--version=8.0.40 drops "MariaDB" from the version string). So MariaDB is
|
||||
the version string or mysql.roles_mapping (10.0.5+), and MySQL 8.0+ is
|
||||
confirmed by mysql.role_edges.
|
||||
|
||||
mysql (MySQL 5.7+) output of SHOW GRANTS and SHOW CREATE USER goes through
|
||||
the translators, as MariaDB rejects some of it.
|
||||
*/
|
||||
struct Source_dialect
|
||||
{
|
||||
bool mariadb, mysql, mysql8, maria_roles;
|
||||
};
|
||||
|
||||
static const Source_dialect &source_dialect()
|
||||
{
|
||||
static Source_dialect d;
|
||||
static bool known= false;
|
||||
if (!known)
|
||||
{
|
||||
unsigned long version= mysql_get_server_version(mysql);
|
||||
d.maria_roles= mysql_table_exists("roles_mapping");
|
||||
d.mariadb= strstr(mysql_get_server_info(mysql), "MariaDB") ||
|
||||
d.maria_roles;
|
||||
d.mysql= !d.mariadb && version >= 50700;
|
||||
d.mysql8= d.mysql && version >= 80000 && mysql_table_exists("role_edges");
|
||||
known= true;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
/*
|
||||
MySQL 8 has no is_role column: an account is a pure role, a pure user, or
|
||||
both (a login account granted to others). MariaDB keeps them apart, so
|
||||
"both" is dumped as a user and as a role of the same name.
|
||||
|
||||
MYSQL8_ROLE_HEURISTIC: the mysql.user shape CREATE ROLE leaves behind.
|
||||
MYSQL8_GRANTED_ONWARD: the account is granted to others, so also a role.
|
||||
|
||||
`t` is the mysql.user alias with its dot ("u.", "mu.").
|
||||
*/
|
||||
#define MYSQL8_ROLE_HEURISTIC(t) \
|
||||
t "account_locked='Y' AND IFNULL(" t "authentication_string, '')='' AND " \
|
||||
t "password_expired='Y'"
|
||||
#define MYSQL8_GRANTED_ONWARD(t) \
|
||||
"EXISTS (SELECT 1 FROM mysql.role_edges ro " \
|
||||
" WHERE ro.FROM_USER=" t "User AND ro.FROM_HOST=" t "Host)"
|
||||
|
||||
/*
|
||||
Worst case of "<QUOTE(user)>@<QUOTE(host)>" with the NUL: QUOTE(s) is up
|
||||
to 2 + 2*strlen(s) bytes. Also sizes the quote_for_equal() buffer.
|
||||
*/
|
||||
static constexpr size_t MAX_QUOTED_USER_HOST_LEN=
|
||||
2*USERNAME_LENGTH + 2*HOSTNAME_LENGTH + 6;
|
||||
|
||||
/* CREATE a role administered by the import role, so the importer can grant it. */
|
||||
static void emit_create_role(const char *quoted_role)
|
||||
{
|
||||
dump_emit_versioned_wrap(md_result_file,
|
||||
opt_replace_into ? "/*M!100103 " : "/*M!100005 ",
|
||||
"CREATE %sROLE %s%s WITH ADMIN mariadb_dump_import_role",
|
||||
opt_replace_into ? "OR REPLACE " : "",
|
||||
opt_ignore ? "IF NOT EXISTS " : "", quoted_role);
|
||||
}
|
||||
|
||||
/*
|
||||
Dump grants for a user or role. For a MySQL role, role_name is its
|
||||
quoted bare name, used as the grantee: a MariaDB role has no host.
|
||||
*/
|
||||
static int dump_grants(const char *user_role, const char *role_name)
|
||||
{
|
||||
DYNAMIC_STRING sqlbuf;
|
||||
MYSQL_ROW row;
|
||||
@@ -4609,20 +4710,32 @@ static int dump_grants(const char *user_role)
|
||||
}
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0]);
|
||||
if (strncmp(row[0], "SET DEFAULT ROLE", sizeof("SET DEFAULT ROLE") - 1) == 0)
|
||||
continue;
|
||||
|
||||
if (source_dialect().mysql)
|
||||
{
|
||||
if (strncmp(row[0], "GRANT ", 6) == 0 &&
|
||||
translate_mysql8_grant(row[0], md_result_file, role_name))
|
||||
continue;
|
||||
if (strncmp(row[0], "REVOKE ", 7) == 0)
|
||||
{
|
||||
translate_mysql8_revoke(row[0], md_result_file);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(md_result_file, "%s;\n", row[0]);
|
||||
}
|
||||
check_io(md_result_file);
|
||||
mysql_free_result(tableres);
|
||||
dynstr_free(&sqlbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dump create user
|
||||
*/
|
||||
|
||||
/* dump CREATE USER, translating MySQL 5.7+ syntax when needed */
|
||||
static int dump_create_user(const char *user)
|
||||
{
|
||||
DYNAMIC_STRING sqlbuf;
|
||||
@@ -4639,9 +4752,24 @@ static int dump_create_user(const char *user)
|
||||
}
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
fprintf(md_result_file, "CREATE %sUSER %s%s;\n", opt_replace_into ? "/*M!100103 OR REPLACE */ ": "",
|
||||
DBUG_ASSERT(row[0]);
|
||||
|
||||
/* Starts with the space translate_mysql8_create_user() expects. */
|
||||
const char *user_str= row[0] + sizeof("CREATE USER") - 1;
|
||||
|
||||
if (source_dialect().mysql)
|
||||
{
|
||||
translate_mysql8_create_user(user_str, md_result_file,
|
||||
opt_replace_into, opt_ignore);
|
||||
check_io(md_result_file);
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(md_result_file, "CREATE %sUSER %s%s;\n",
|
||||
opt_replace_into ? "/*M!100103 OR REPLACE */ " : "",
|
||||
opt_ignore ? "IF NOT EXISTS " : "",
|
||||
row[0] + sizeof("CREATE USER"));
|
||||
user_str + 1);
|
||||
check_io(md_result_file);
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
dynstr_free(&sqlbuf);
|
||||
@@ -4650,57 +4778,359 @@ static int dump_create_user(const char *user)
|
||||
|
||||
|
||||
/*
|
||||
dump all users, roles and their grants
|
||||
Dialect-specific helpers for dump_all_users_roles_and_grants(): role
|
||||
hierarchy and default roles each have a _maria and a _mysql variant.
|
||||
The dump_* helpers return 0 on success, 1 on error.
|
||||
*/
|
||||
|
||||
static void init_role_set(HASH *set)
|
||||
{
|
||||
if (my_hash_init(PSI_NOT_INSTRUMENTED, set, &my_charset_bin, 16, 0, 0,
|
||||
get_table_key, my_free, 0))
|
||||
die(EX_EOM, "Couldn't allocate memory for the role set");
|
||||
}
|
||||
|
||||
/*
|
||||
Mark `role` (the QUOTE()'d name) as seen; true only the first time. The
|
||||
recursive walk can return a role at several non-adjacent depths.
|
||||
*/
|
||||
static bool first_role_sighting(HASH *seen, const char *role)
|
||||
{
|
||||
if (my_hash_search(seen, reinterpret_cast<const uchar*>(role), strlen(role)))
|
||||
return false;
|
||||
char *copy= my_strdup(PSI_NOT_INSTRUMENTED, role, MYF(MY_WME));
|
||||
if (!copy || my_hash_insert(seen, reinterpret_cast<uchar*>(copy)))
|
||||
die(EX_EOM, "Couldn't allocate memory for the role set");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
Emit CREATE ROLE once per MariaDB role reachable through an admin chain,
|
||||
and a GRANT per (role, grantee, admin) tuple. ORDER BY n creates a
|
||||
grantee role before it is a GRANT target.
|
||||
*/
|
||||
static int dump_role_hierarchy_maria()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"WITH RECURSIVE create_role_order AS"
|
||||
" (SELECT 1 as n, roles_mapping.*"
|
||||
" FROM mysql.roles_mapping"
|
||||
" JOIN mysql.user USING (user,host)"
|
||||
" WHERE is_role='N'"
|
||||
" AND Admin_option='Y'"
|
||||
" UNION SELECT c.n+1, r.*"
|
||||
" FROM create_role_order c"
|
||||
" JOIN mysql.roles_mapping r ON c.role=r.user"
|
||||
" AND r.host=''"
|
||||
" AND r.Admin_option='Y') "
|
||||
"SELECT QUOTE(ROLE) AS r,"
|
||||
" CONCAT(QUOTE(user),"
|
||||
" IF(HOST='', '', CONCAT('@', QUOTE(HOST)))) AS c,"
|
||||
" Admin_option "
|
||||
"FROM create_role_order ORDER BY n, r, user"))
|
||||
return 1;
|
||||
|
||||
HASH seen;
|
||||
init_role_set(&seen);
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && row[1] && row[2]);
|
||||
if (first_role_sighting(&seen, row[0]))
|
||||
{
|
||||
emit_create_role(row[0]);
|
||||
}
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100005 ",
|
||||
"GRANT %s TO %s%s",
|
||||
row[0], row[1],
|
||||
(row[2][0] == 'Y') ? " WITH ADMIN OPTION" : "");
|
||||
}
|
||||
check_io(md_result_file);
|
||||
my_hash_free(&seen);
|
||||
mysql_free_result(tableres);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Emit CREATE ROLE for every MySQL role (see MYSQL8_ROLE_HEURISTIC), then a
|
||||
GRANT per distinct (role, grantee, admin option) of role_edges.
|
||||
|
||||
Every role is created WITH ADMIN mariadb_dump_import_role, so all CREATE
|
||||
ROLEs precede the grants and no hierarchy order is needed. MySQL roles
|
||||
differing only in host map to one hostless role (GROUP BY User).
|
||||
|
||||
The TO side of a GRANT is a user, a role or both (MYSQL8_GRANTED_ONWARD):
|
||||
`GRANT r TO user@host`, `GRANT r TO role`, or both.
|
||||
*/
|
||||
static int dump_role_hierarchy_mysql()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT QUOTE(u.User), COUNT(*),"
|
||||
" MIN(" MYSQL8_ROLE_HEURISTIC("u.") " AND NOT "
|
||||
MYSQL8_GRANTED_ONWARD("u.") "),"
|
||||
" GROUP_CONCAT(QUOTE(u.Host) ORDER BY u.Host) "
|
||||
"FROM mysql.user u "
|
||||
"WHERE " MYSQL8_ROLE_HEURISTIC("u.")
|
||||
" OR " MYSQL8_GRANTED_ONWARD("u.")
|
||||
" GROUP BY u.User ORDER BY u.User"))
|
||||
return 1;
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && row[1] && row[2] && row[3]);
|
||||
/*
|
||||
A role granted to nobody is inferred by heuristic alone: a disabled
|
||||
user account of the same shape looks the same.
|
||||
*/
|
||||
if (row[2][0] == '1')
|
||||
dump_emit_comment_warning(md_result_file,
|
||||
"WARNING: MySQL account %s (hosts %s) is locked, passwordless, "
|
||||
"expired and granted to nobody; dumped as CREATE ROLE (MySQL 8 has "
|
||||
"no role flag), recreate it as a user if it was a disabled account",
|
||||
row[0], row[3]);
|
||||
if (strcmp(row[1], "1"))
|
||||
dump_emit_comment_warning(md_result_file,
|
||||
"WARNING: %s MySQL roles named %s on different hosts (%s) map onto "
|
||||
"one hostless MariaDB role; they are merged", row[1], row[0], row[3]);
|
||||
emit_create_role(row[0]);
|
||||
}
|
||||
check_io(md_result_file);
|
||||
mysql_free_result(tableres);
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT DISTINCT QUOTE(re.FROM_USER),"
|
||||
" QUOTE(re.TO_USER), QUOTE(re.TO_HOST),"
|
||||
" re.WITH_ADMIN_OPTION,"
|
||||
" (" MYSQL8_ROLE_HEURISTIC("tu.") ") AS to_is_role,"
|
||||
" (NOT (" MYSQL8_ROLE_HEURISTIC("tu.") ") AND "
|
||||
MYSQL8_GRANTED_ONWARD("tu.") ") AS to_is_hybrid "
|
||||
"FROM mysql.role_edges re "
|
||||
"LEFT JOIN mysql.user tu ON tu.User=re.TO_USER AND tu.Host=re.TO_HOST "
|
||||
"ORDER BY re.FROM_USER, re.TO_USER, re.TO_HOST, re.WITH_ADMIN_OPTION"))
|
||||
return 1;
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && row[1] && row[2] && row[3]);
|
||||
if (!row[4])
|
||||
{
|
||||
dump_emit_comment_warning(md_result_file,
|
||||
"WARNING: role %s is granted to %s@%s, which is not in mysql.user; "
|
||||
"the grant is skipped", row[0], row[1], row[2]);
|
||||
continue;
|
||||
}
|
||||
const char *admin= (row[3][0] == 'Y') ? " WITH ADMIN OPTION" : "";
|
||||
bool to_is_role= (row[4][0] == '1');
|
||||
bool to_is_hybrid= (row[5][0] == '1');
|
||||
|
||||
if (to_is_role || to_is_hybrid)
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100005 ",
|
||||
"GRANT %s TO %s%s", row[0], row[1], admin);
|
||||
if (!to_is_role)
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100005 ",
|
||||
"GRANT %s TO %s@%s%s", row[0], row[1], row[2], admin);
|
||||
}
|
||||
check_io(md_result_file);
|
||||
mysql_free_result(tableres);
|
||||
|
||||
/* Mandatory roles are implicit: they have no role_edges row */
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT @@GLOBAL.mandatory_roles"))
|
||||
return 1;
|
||||
if ((row= mysql_fetch_row(tableres)) && row[0] && row[0][0])
|
||||
dump_emit_comment_warning(md_result_file,
|
||||
"WARNING: mandatory_roles is %s: these roles are granted to every "
|
||||
"user implicitly and MariaDB has no equivalent; grant them explicitly "
|
||||
"if needed", row[0]);
|
||||
mysql_free_result(tableres);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* MariaDB users + single default role: GRANTS + SET DEFAULT ROLE */
|
||||
static int dump_default_roles_maria()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
int result= 0;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"select IF(default_role='', 'NONE', QUOTE(default_role)) as r,"
|
||||
"concat(QUOTE(User), '@', QUOTE(Host)) as u FROM mysql.user "
|
||||
"/*M!100005 WHERE is_role='N' */"))
|
||||
return 1;
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && row[1]);
|
||||
if (dump_grants(row[1], NULL))
|
||||
result= 1;
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100005 ",
|
||||
"SET DEFAULT ROLE %s FOR %s", row[0], row[1]);
|
||||
}
|
||||
check_io(md_result_file);
|
||||
mysql_free_result(tableres);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
MySQL users + default roles. A user with several default roles yields
|
||||
several rows; ORDER BY groups them, so GRANTS are emitted once per user.
|
||||
MariaDB allows one default role: the first is emitted, extras get a
|
||||
# WARNING. The default role's host is ignored (a MariaDB role has none).
|
||||
*/
|
||||
static int dump_default_roles_mysql()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
int result= 0;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT IF(DEFAULT_ROLE_HOST IS NULL, 'NONE', QUOTE(DEFAULT_ROLE_USER)),"
|
||||
" QUOTE(mu.USER), QUOTE(mu.HOST) "
|
||||
"FROM mysql.user mu LEFT JOIN mysql.default_roles USING (USER, HOST) "
|
||||
"WHERE NOT (" MYSQL8_ROLE_HEURISTIC("mu.") ") "
|
||||
"ORDER BY mu.USER, mu.HOST, DEFAULT_ROLE_USER, DEFAULT_ROLE_HOST"))
|
||||
return 1;
|
||||
|
||||
/* The rows of a stored result stay valid, so keep the previous user's. */
|
||||
const char *last_user= NULL, *last_host= NULL;
|
||||
bool default_role_emitted= false; /* one already emitted for last_user */
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && row[1] && row[2]);
|
||||
const char *r_user= row[0];
|
||||
const char *u_user= row[1], *u_host= row[2];
|
||||
|
||||
if (!last_user || strcmp(u_user, last_user) || strcmp(u_host, last_host))
|
||||
{
|
||||
char user_host[MAX_QUOTED_USER_HOST_LEN];
|
||||
my_snprintf(user_host, sizeof(user_host), "%s@%s", u_user, u_host);
|
||||
if (dump_grants(user_host, NULL))
|
||||
result= 1;
|
||||
last_user= u_user;
|
||||
last_host= u_host;
|
||||
default_role_emitted= false;
|
||||
}
|
||||
|
||||
if (strcmp(r_user, "NONE") == 0)
|
||||
continue;
|
||||
|
||||
if (default_role_emitted)
|
||||
dump_emit_comment_warning(md_result_file,
|
||||
"WARNING: %s@%s has multiple default roles in the source; "
|
||||
"MariaDB supports a single default role, dropping %s",
|
||||
u_user, u_host, r_user);
|
||||
else
|
||||
{
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100005 ",
|
||||
"SET DEFAULT ROLE %s FOR %s@%s", r_user, u_user, u_host);
|
||||
default_role_emitted= true;
|
||||
}
|
||||
}
|
||||
check_io(md_result_file);
|
||||
mysql_free_result(tableres);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Run `query` and dump SHOW GRANTS for each row: column 0 is the quoted
|
||||
grantee ('r' or 'r'@'h'). With has_role_name, column 1 is the role's
|
||||
quoted bare name, which becomes the grantee of its translated GRANTs.
|
||||
*/
|
||||
static int dump_grants_for_roles(const char *query, bool has_role_name)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
int result= 0;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres, query))
|
||||
return 1;
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
DBUG_ASSERT(row[0] && (!has_role_name || row[1]));
|
||||
if (dump_grants(row[0], has_role_name ? row[1] : NULL))
|
||||
result= 1;
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* dump all users, roles, and their grants */
|
||||
static int dump_all_users_roles_and_grants()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *tableres;
|
||||
int result= 0;
|
||||
/* Roles added in MariaDB-10.0.5 or MySQL-8.0 */
|
||||
my_bool maria_roles_exist= (mysql_get_server_version(mysql) >= 100005);
|
||||
my_bool mysql_roles_exist= (mysql_get_server_version(mysql) >= 80001) && !maria_roles_exist;
|
||||
bool mysql_roles_exist= source_dialect().mysql8;
|
||||
bool maria_roles_exist= source_dialect().maria_roles;
|
||||
bool roles_exist= maria_roles_exist || mysql_roles_exist;
|
||||
|
||||
if (mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT CONCAT(QUOTE(u.user), '@', QUOTE(u.Host)) AS u "
|
||||
"SELECT CONCAT(QUOTE(u.user), '@', QUOTE(u.Host)) AS u, "
|
||||
"u.user, u.Host "
|
||||
"FROM mysql.user u "
|
||||
" /*!80001 LEFT JOIN mysql.role_edges e "
|
||||
" ON u.user=e.from_user "
|
||||
" AND u.host=e.from_host "
|
||||
" WHERE e.from_user IS NULL */"
|
||||
" /*!80000 WHERE NOT ("
|
||||
MYSQL8_ROLE_HEURISTIC("u.") ") */"
|
||||
" /*M!100005 WHERE is_role='N' */"))
|
||||
return 1;
|
||||
if (opt_replace_into)
|
||||
{
|
||||
/*
|
||||
Self-DROP guards come first, so the import stops at the current user
|
||||
before any account is dropped. current_user() is compared with the raw
|
||||
user@host: row[0] is QUOTE'd and would not match.
|
||||
*/
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
char buf[MAX_QUOTED_USER_HOST_LEN];
|
||||
char raw_uh[USERNAME_LENGTH + HOSTNAME_LENGTH + 2];
|
||||
my_snprintf(raw_uh, sizeof(raw_uh), "%s@%s", row[1], row[2]);
|
||||
/* Fixed MESSAGE_TEXT: the failing line number identifies the guard. */
|
||||
fputs("DELIMITER |\n"
|
||||
"/*M!100101 IF current_user()=", md_result_file);
|
||||
dump_put_comment_safe_literal(md_result_file,
|
||||
quote_for_equal(raw_uh, buf));
|
||||
fputs(" THEN\n"
|
||||
" SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001,"
|
||||
" MESSAGE_TEXT='Cannot remove the current user during import';\n"
|
||||
"END IF */|\n"
|
||||
"DELIMITER ;\n", md_result_file);
|
||||
}
|
||||
mysql_data_seek(tableres, 0);
|
||||
}
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
char buf[200];
|
||||
if (opt_replace_into)
|
||||
/* Protection against removing the current import user */
|
||||
/* MySQL-8.0 export capability */
|
||||
fprintf(md_result_file,
|
||||
"DELIMITER |\n"
|
||||
"/*M!100101 IF current_user()=%s THEN\n"
|
||||
" SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001,"
|
||||
" MESSAGE_TEXT=\"Don't remove current user %s'\";\n"
|
||||
"END IF */|\n"
|
||||
"DELIMITER ;\n"
|
||||
"/*!50701 DROP USER IF EXISTS %s */;\n",
|
||||
quote_for_equal(row[0],buf), row[0], row[0]);
|
||||
dump_emit_versioned_wrap(md_result_file, "/*M!100103 ",
|
||||
"DROP USER IF EXISTS %s", row[0]);
|
||||
if (dump_create_user(row[0]))
|
||||
result= 1;
|
||||
/* if roles exist, defer dumping grants until after roles created */
|
||||
if (maria_roles_exist || mysql_roles_exist)
|
||||
if (roles_exist)
|
||||
continue;
|
||||
if (dump_grants(row[0]))
|
||||
if (dump_grants(row[0], NULL))
|
||||
result= 1;
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
|
||||
if (!(maria_roles_exist || mysql_roles_exist))
|
||||
goto exit;
|
||||
if (!roles_exist)
|
||||
return result;
|
||||
|
||||
/*
|
||||
Preserve current role active role, in case this dump is imported
|
||||
Preserve the current active role, in case this dump is imported
|
||||
in the same connection that assumes the active role at the beginning
|
||||
is the same as at the end of the connection. This is so:
|
||||
|
||||
@@ -4719,134 +5149,61 @@ static int dump_all_users_roles_and_grants()
|
||||
have the necessary privileges to grant users to a created role or to
|
||||
create new admins for the created role.
|
||||
|
||||
At the end of the import the mariadb_dump_import_role is be dropped,
|
||||
which implicitly drops all its admin aspects of the dropped role.
|
||||
At the end of the import the mariadb_dump_import_role is dropped,
|
||||
which implicitly drops all its admin grants on the new roles.
|
||||
This is significantly easier than revoking the ADMIN of each role
|
||||
from the current user.
|
||||
*/
|
||||
fputs("SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;\n"
|
||||
"CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;\n"
|
||||
"GRANT mariadb_dump_import_role TO CURRENT_USER();\n"
|
||||
"GRANT mariadb_dump_import_role TO CURRENT_USER();\n"
|
||||
"SET ROLE mariadb_dump_import_role;\n"
|
||||
, md_result_file);
|
||||
/* No show create role yet, MDEV-22311 */
|
||||
/* Roles, with user admins first, then roles they administer, and recurse on that */
|
||||
if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"WITH RECURSIVE create_role_order AS"
|
||||
" (SELECT 1 as n, roles_mapping.*"
|
||||
" FROM mysql.roles_mapping"
|
||||
" JOIN mysql.user USING (user,host)"
|
||||
" WHERE is_role='N'"
|
||||
" AND Admin_option='Y'"
|
||||
" UNION SELECT c.n+1, r.*"
|
||||
" FROM create_role_order c"
|
||||
" JOIN mysql.roles_mapping r ON c.role=r.user"
|
||||
" AND r.host=''"
|
||||
" AND r.Admin_option='Y') "
|
||||
"SELECT QUOTE(ROLE) AS r,"
|
||||
" CONCAT(QUOTE(user),"
|
||||
" IF(HOST='', '', CONCAT('@', QUOTE(HOST)))) AS c,"
|
||||
" Admin_option "
|
||||
"FROM create_role_order ORDER BY n, r, user"))
|
||||
return 1;
|
||||
/*
|
||||
TODO Mysql - misses roles that have no admin or role members.
|
||||
MySQL roles don't require an admin.
|
||||
On the roles path, a GRANT to an account that was not created (a role
|
||||
classified wrongly, or a name whose CREATE was refused) must fail
|
||||
instead of creating a passwordless login.
|
||||
*/
|
||||
if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"WITH RECURSIVE create_role_order AS"
|
||||
" (SELECT 1 AS n,"
|
||||
" re.*"
|
||||
" FROM mysql.role_edges re"
|
||||
" JOIN mysql.user u ON re.TO_HOST=u.HOST"
|
||||
" AND re.TO_USER = u.USER"
|
||||
" LEFT JOIN mysql.role_edges re2 ON re.TO_USER=re2.FROM_USER"
|
||||
" AND re2.TO_HOST=re2.FROM_HOST"
|
||||
" WHERE re2.FROM_USER IS NULL"
|
||||
" UNION SELECT c.n+1,"
|
||||
" re.*"
|
||||
" FROM create_role_order c"
|
||||
" JOIN mysql.role_edges re ON c.FROM_USER=re.TO_USER"
|
||||
" AND c.FROM_HOST=re.TO_HOST) "
|
||||
"SELECT CONCAT(QUOTE(FROM_USER), '/*!80001 @', QUOTE(FROM_HOST), '*/') AS r,"
|
||||
" CONCAT(QUOTE(TO_USER), IF(n=1, CONCAT('@', QUOTE(TO_HOST)),"
|
||||
" CONCAT('/*!80001 @', QUOTE(TO_HOST), ' */'))) AS u,"
|
||||
" WITH_ADMIN_OPTION "
|
||||
"FROM create_role_order "
|
||||
"ORDER BY n,"
|
||||
" FROM_USER,"
|
||||
" FROM_HOST,"
|
||||
" TO_USER,"
|
||||
" TO_HOST,"
|
||||
" WITH_ADMIN_OPTION"))
|
||||
return 1;
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
/* MySQL-8.0 export capability */
|
||||
if (opt_replace_into)
|
||||
fprintf(md_result_file,
|
||||
"/*!80001 DROP ROLE IF EXISTS %s */;\n", row[0]);
|
||||
fprintf(md_result_file,
|
||||
"/*!80001 CREATE ROLE %s%s */;\n", opt_ignore ? "IF NOT EXISTS " : "", row[0]);
|
||||
/* By default created with current role */
|
||||
fprintf(md_result_file,
|
||||
"%sROLE %s%s WITH ADMIN mariadb_dump_import_role */;\n",
|
||||
opt_replace_into ? "/*M!100103 CREATE OR REPLACE ": "/*M!100005 CREATE ",
|
||||
opt_ignore ? "IF NOT EXISTS " : "", row[0]);
|
||||
fprintf(md_result_file, "/*M!100005 GRANT %s TO %s%s*/;\n",
|
||||
row[0], row[1], (row[2][0] == 'Y') ? " WITH ADMIN OPTION " : "");
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
fputs("/*M!100005 SET @mariadb_dump_saved_sql_mode=@@sql_mode,\n"
|
||||
" sql_mode=CONCAT_WS(',', NULLIF(@@sql_mode, ''),"
|
||||
" 'NO_AUTO_CREATE_USER') */;\n", md_result_file);
|
||||
|
||||
/* users and their default role */
|
||||
if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"select IF(default_role='', 'NONE', QUOTE(default_role)) as r,"
|
||||
"concat(QUOTE(User), '@', QUOTE(Host)) as u FROM mysql.user "
|
||||
"/*M!100005 WHERE is_role='N' */"))
|
||||
return 1;
|
||||
if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT IF(DEFAULT_ROLE_HOST IS NULL, 'NONE', CONCAT(QUOTE(DEFAULT_ROLE_USER),"
|
||||
" '@', QUOTE(DEFAULT_ROLE_HOST))) as r,"
|
||||
" CONCAT(QUOTE(mu.USER),'@',QUOTE(mu.HOST)) as u "
|
||||
"FROM mysql.user mu LEFT JOIN mysql.default_roles using (USER, HOST)"))
|
||||
/* No show create role yet, MDEV-22311 */
|
||||
if (maria_roles_exist)
|
||||
{
|
||||
mysql_free_result(tableres);
|
||||
return 1;
|
||||
result|= dump_role_hierarchy_maria();
|
||||
result|= dump_default_roles_maria();
|
||||
/* GRANTS for every role with at least one admin. */
|
||||
result|= dump_grants_for_roles(
|
||||
"SELECT DISTINCT QUOTE(m.role) AS r "
|
||||
" FROM mysql.roles_mapping m"
|
||||
" JOIN mysql.user u ON u.user = m.role"
|
||||
" WHERE is_role='Y'"
|
||||
" AND Admin_option='Y'"
|
||||
" ORDER BY m.role", false);
|
||||
}
|
||||
else /* mysql_roles_exist */
|
||||
{
|
||||
result|= dump_role_hierarchy_mysql();
|
||||
result|= dump_default_roles_mysql();
|
||||
/*
|
||||
GRANTS for every MySQL role, including those granted to nobody. A
|
||||
hybrid's user grants come from dump_default_roles_mysql().
|
||||
*/
|
||||
result|= dump_grants_for_roles(
|
||||
"SELECT CONCAT(QUOTE(u.User), '@', QUOTE(u.Host)), QUOTE(u.User) "
|
||||
"FROM mysql.user u "
|
||||
"WHERE " MYSQL8_ROLE_HEURISTIC("u.")
|
||||
" OR " MYSQL8_GRANTED_ONWARD("u.")
|
||||
" ORDER BY u.User, u.Host", true);
|
||||
}
|
||||
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
if (dump_grants(row[1]))
|
||||
result= 1;
|
||||
fprintf(md_result_file, "/*M!100005 SET DEFAULT ROLE %s FOR %s */;\n", row[0], row[1]);
|
||||
fprintf(md_result_file, "/*!80001 ALTER USER %s DEFAULT ROLE %s */;\n", row[1], row[0]);
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
|
||||
if (maria_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT DISTINCT QUOTE(m.role) AS r "
|
||||
" FROM mysql.roles_mapping m"
|
||||
" JOIN mysql.user u ON u.user = m.role"
|
||||
" WHERE is_role='Y'"
|
||||
" AND Admin_option='Y'"
|
||||
" ORDER BY m.role"))
|
||||
return 1;
|
||||
if (mysql_roles_exist && mysql_query_with_error_report(mysql, &tableres,
|
||||
"SELECT DISTINCT CONCAT(QUOTE(FROM_USER),'@', QUOTE(FROM_HOST)) AS r "
|
||||
"FROM mysql.role_edges"))
|
||||
return 1;
|
||||
while ((row= mysql_fetch_row(tableres)))
|
||||
{
|
||||
if (dump_grants(row[0]))
|
||||
result= 1;
|
||||
}
|
||||
mysql_free_result(tableres);
|
||||
/* switch back */
|
||||
fputs("/*M!100005 SET sql_mode=@mariadb_dump_saved_sql_mode */;\n",
|
||||
md_result_file);
|
||||
fputs("SET ROLE NONE;\n"
|
||||
"DROP ROLE mariadb_dump_import_role;\n"
|
||||
"/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;\n",
|
||||
md_result_file);
|
||||
exit:
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
17
mysql-test/include/backup_priv_tables.inc
Normal file
17
mysql-test/include/backup_priv_tables.inc
Normal file
@@ -0,0 +1,17 @@
|
||||
# Snapshot the privilege tables; restore with include/restore_priv_tables.inc
|
||||
--disable_query_log
|
||||
CREATE TABLE backup_global_priv LIKE mysql.global_priv;
|
||||
CREATE TABLE backup_roles_mapping LIKE mysql.roles_mapping;
|
||||
CREATE TABLE backup_tables_priv LIKE mysql.tables_priv;
|
||||
CREATE TABLE backup_columns_priv LIKE mysql.columns_priv;
|
||||
CREATE TABLE backup_procs_priv LIKE mysql.procs_priv;
|
||||
CREATE TABLE backup_proxies_priv LIKE mysql.proxies_priv;
|
||||
CREATE TABLE backup_db LIKE mysql.db;
|
||||
INSERT INTO backup_global_priv SELECT * FROM mysql.global_priv;
|
||||
INSERT INTO backup_roles_mapping SELECT * FROM mysql.roles_mapping;
|
||||
INSERT INTO backup_tables_priv SELECT * FROM mysql.tables_priv;
|
||||
INSERT INTO backup_columns_priv SELECT * FROM mysql.columns_priv;
|
||||
INSERT INTO backup_procs_priv SELECT * FROM mysql.procs_priv;
|
||||
INSERT INTO backup_proxies_priv SELECT * FROM mysql.proxies_priv;
|
||||
INSERT INTO backup_db SELECT * FROM mysql.db;
|
||||
--enable_query_log
|
||||
20
mysql-test/include/restore_priv_tables.inc
Normal file
20
mysql-test/include/restore_priv_tables.inc
Normal file
@@ -0,0 +1,20 @@
|
||||
# Restore the snapshot taken by include/backup_priv_tables.inc
|
||||
--disable_query_log
|
||||
DELETE FROM mysql.global_priv;
|
||||
DELETE FROM mysql.roles_mapping;
|
||||
DELETE FROM mysql.tables_priv;
|
||||
DELETE FROM mysql.columns_priv;
|
||||
DELETE FROM mysql.procs_priv;
|
||||
DELETE FROM mysql.proxies_priv;
|
||||
DELETE FROM mysql.db;
|
||||
INSERT INTO mysql.global_priv SELECT * FROM backup_global_priv;
|
||||
INSERT INTO mysql.roles_mapping SELECT * FROM backup_roles_mapping;
|
||||
INSERT INTO mysql.tables_priv SELECT * FROM backup_tables_priv;
|
||||
INSERT INTO mysql.columns_priv SELECT * FROM backup_columns_priv;
|
||||
INSERT INTO mysql.procs_priv SELECT * FROM backup_procs_priv;
|
||||
INSERT INTO mysql.proxies_priv SELECT * FROM backup_proxies_priv;
|
||||
INSERT INTO mysql.db SELECT * FROM backup_db;
|
||||
DROP TABLE backup_global_priv, backup_roles_mapping, backup_tables_priv,
|
||||
backup_columns_priv, backup_procs_priv, backup_proxies_priv, backup_db;
|
||||
FLUSH PRIVILEGES;
|
||||
--enable_query_log
|
||||
143
mysql-test/main/mariadb-dump-roles-regression.result
Normal file
143
mysql-test/main/mariadb-dump-roles-regression.result
Normal file
@@ -0,0 +1,143 @@
|
||||
# MDEV-24557 mariadb-dump: import MySQL 5.7 and 8.x users, grants, roles
|
||||
#
|
||||
# A role hierarchy dumped and restored by mariadb-dump --system=users;
|
||||
# a second dump must be identical.
|
||||
#
|
||||
#
|
||||
# Hierarchy:
|
||||
# admin_role -- admin of mid_role
|
||||
# mid_role -- admin of leaf_role
|
||||
# leaf_role -- granted to test_user (default); administered by
|
||||
# mid_role (depth 3) and test_user (depth 1), so the
|
||||
# role is reached at two non-adjacent depths
|
||||
# plain_role -- granted to test_user (non-default), administered by
|
||||
# test_user and root: two admin rows for one role
|
||||
#
|
||||
CREATE USER test_user@localhost IDENTIFIED BY 'pw';
|
||||
CREATE ROLE admin_role;
|
||||
CREATE ROLE mid_role WITH ADMIN admin_role;
|
||||
CREATE ROLE leaf_role WITH ADMIN mid_role;
|
||||
CREATE ROLE plain_role;
|
||||
GRANT SELECT ON *.* TO admin_role;
|
||||
GRANT INSERT, UPDATE ON *.* TO mid_role;
|
||||
GRANT SHOW DATABASES ON *.* TO leaf_role;
|
||||
GRANT PROCESS ON *.* TO plain_role;
|
||||
GRANT leaf_role TO test_user@localhost WITH ADMIN OPTION;
|
||||
GRANT plain_role TO test_user@localhost WITH ADMIN OPTION;
|
||||
SET DEFAULT ROLE leaf_role FOR test_user@localhost;
|
||||
DROP USER test_user@localhost;
|
||||
DROP ROLE plain_role;
|
||||
DROP ROLE leaf_role;
|
||||
DROP ROLE mid_role;
|
||||
DROP ROLE admin_role;
|
||||
CREATE USER test_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO test_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO test_restore WITH GRANT OPTION;
|
||||
# The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='test_restore' OR Role='mariadb_dump_import_role';
|
||||
User Role
|
||||
DROP USER test_restore;
|
||||
# A second dump is identical: users, grants, roles, admin options and
|
||||
# default roles survived.
|
||||
# The restored state is the hierarchy above, so an omission cannot pass:
|
||||
SELECT User, Host, Role, Admin_option FROM mysql.roles_mapping
|
||||
WHERE Role LIKE '%\\_role' ORDER BY Role, User, Host;
|
||||
User Host Role Admin_option
|
||||
root localhost admin_role Y
|
||||
mid_role leaf_role Y
|
||||
test_user localhost leaf_role Y
|
||||
admin_role mid_role Y
|
||||
root localhost plain_role Y
|
||||
test_user localhost plain_role Y
|
||||
SELECT User, Host, default_role FROM mysql.user WHERE User='test_user';
|
||||
User Host default_role
|
||||
test_user localhost leaf_role
|
||||
#
|
||||
# --replace import refuses to run as the user it would drop
|
||||
#
|
||||
CREATE USER test_self@localhost IDENTIFIED BY 'pw';
|
||||
CREATE USER test_other@localhost IDENTIFIED BY 'pw';
|
||||
# the whole dump, run by the guarded user: refused by the guard, not
|
||||
# by a DROP USER privilege error
|
||||
FOUND 1 /(?:\A|\n)ERROR 30001 \(45000\) at line \d+: Cannot remove the current user during import/ in guard_self.err
|
||||
# the guard alone, run by the guarded user: refused
|
||||
FOUND 1 /(?:\A|\n)ERROR 30001 \(45000\) at line \d+: Cannot remove the current user during import/ in guard_self.err
|
||||
# any other user: passes
|
||||
DROP USER test_self@localhost;
|
||||
DROP USER test_other@localhost;
|
||||
#
|
||||
# A name with a comment end is split over string literals in an
|
||||
# EXECUTE IMMEDIATE, not emitted inside a /*M!...*/ comment.
|
||||
#
|
||||
CREATE USER test_ss@localhost IDENTIFIED BY 'pw';
|
||||
CREATE USER `u_ss*/x`@localhost IDENTIFIED BY 'pw';
|
||||
CREATE ROLE `r_ss*/x`;
|
||||
CREATE ROLE r_admin;
|
||||
GRANT SELECT ON *.* TO `r_ss*/x`;
|
||||
GRANT `r_ss*/x` TO r_admin WITH ADMIN OPTION;
|
||||
GRANT `r_ss*/x` TO test_ss@localhost WITH ADMIN OPTION;
|
||||
# also with a newline, a quote and a backslash
|
||||
CREATE ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
# no version comment line holds a comment end before its own (expect NOT FOUND):
|
||||
NOT FOUND /(?:\A|\n)/\*M![^\n]*\*/[^\n]*\*// in dump_starslash.sql
|
||||
# the role and the user are emitted split (expect FOUND):
|
||||
FOUND 1 /EXECUTE IMMEDIATE CONCAT\('CREATE OR REPLACE ROLE ''r_ss\*', '/x''/ in dump_starslash.sql
|
||||
FOUND 1 /EXECUTE IMMEDIATE CONCAT\('DROP USER IF EXISTS [^\n]*u_ss\*', '/x/ in dump_starslash.sql
|
||||
FOUND 1 /current_user\(\)=CONCAT\('u_ss\*', '/x@localhost'\)/ in dump_starslash.sql
|
||||
# the dump disables account auto-creation (expect FOUND):
|
||||
FOUND 1 /sql_mode=CONCAT_WS\(',', NULLIF\(\@\@sql_mode, ''\), 'NO_AUTO_CREATE_USER'\)/ in dump_starslash.sql
|
||||
# the dump imports and recreates the names intact:
|
||||
DROP USER test_ss@localhost;
|
||||
DROP USER `u_ss*/x`@localhost;
|
||||
DROP ROLE `r_ss*/x`;
|
||||
DROP ROLE r_admin;
|
||||
DROP ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
CREATE USER test_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO test_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO test_restore WITH GRANT OPTION;
|
||||
DROP USER test_restore;
|
||||
SELECT user AS User, host AS Host, is_role FROM mysql.user
|
||||
WHERE user LIKE 'u\_ss%' OR user LIKE 'r\_ss%' OR user LIKE 'r\_nl%'
|
||||
ORDER BY user;
|
||||
User Host is_role
|
||||
r_nl*/
|
||||
INJECTED_NL_MARKER'\ Y
|
||||
r_ss*/x Y
|
||||
u_ss*/x localhost N
|
||||
SHOW GRANTS FOR `r_ss*/x`;
|
||||
Grants for r_ss*/x
|
||||
GRANT SELECT ON *.* TO `r_ss*/x`
|
||||
SHOW GRANTS FOR test_ss@localhost;
|
||||
Grants for test_ss@localhost
|
||||
GRANT `r_ss*/x` TO `test_ss`@`localhost` WITH ADMIN OPTION
|
||||
GRANT USAGE ON *.* TO `test_ss`@`localhost` IDENTIFIED BY PASSWORD '*D821809F681A40A6E379B50D0463EFAE20BDD122'
|
||||
DROP USER test_ss@localhost;
|
||||
DROP USER `u_ss*/x`@localhost;
|
||||
DROP ROLE `r_ss*/x`;
|
||||
DROP ROLE r_admin;
|
||||
DROP ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
#
|
||||
# A server whose version string has no "MariaDB" is still a MariaDB
|
||||
# source: roles, their grants and default roles are dumped
|
||||
#
|
||||
# restart: --version=8.0.40
|
||||
CREATE ROLE r_ver;
|
||||
CREATE USER u_ver@localhost;
|
||||
GRANT SELECT ON *.* TO r_ver;
|
||||
GRANT r_ver TO u_ver@localhost;
|
||||
SET DEFAULT ROLE r_ver FOR u_ver@localhost;
|
||||
FOUND 2 /ROLE 'r_ver'/ in dump_version.sql
|
||||
FOUND 1 /GRANT `r_ver` TO `u_ver`@`localhost`/ in dump_version.sql
|
||||
FOUND 1 /SET DEFAULT ROLE 'r_ver' FOR 'u_ver'@'localhost'/ in dump_version.sql
|
||||
# a table named mysql.role_edges does not make it look like MySQL 8:
|
||||
# the dump is the same
|
||||
CREATE TABLE mysql.role_edges (FROM_HOST CHAR(255));
|
||||
DROP TABLE mysql.role_edges;
|
||||
DROP USER u_ver@localhost;
|
||||
DROP ROLE r_ver;
|
||||
# restart
|
||||
# End of 13.1 tests
|
||||
219
mysql-test/main/mariadb-dump-roles-regression.test
Normal file
219
mysql-test/main/mariadb-dump-roles-regression.test
Normal file
@@ -0,0 +1,219 @@
|
||||
--source include/not_embedded.inc
|
||||
|
||||
--echo # MDEV-24557 mariadb-dump: import MySQL 5.7 and 8.x users, grants, roles
|
||||
--echo #
|
||||
--echo # A role hierarchy dumped and restored by mariadb-dump --system=users;
|
||||
--echo # a second dump must be identical.
|
||||
--echo #
|
||||
|
||||
--source include/backup_priv_tables.inc
|
||||
|
||||
--echo #
|
||||
--echo # Hierarchy:
|
||||
--echo # admin_role -- admin of mid_role
|
||||
--echo # mid_role -- admin of leaf_role
|
||||
--echo # leaf_role -- granted to test_user (default); administered by
|
||||
--echo # mid_role (depth 3) and test_user (depth 1), so the
|
||||
--echo # role is reached at two non-adjacent depths
|
||||
--echo # plain_role -- granted to test_user (non-default), administered by
|
||||
--echo # test_user and root: two admin rows for one role
|
||||
--echo #
|
||||
CREATE USER test_user@localhost IDENTIFIED BY 'pw';
|
||||
CREATE ROLE admin_role;
|
||||
CREATE ROLE mid_role WITH ADMIN admin_role;
|
||||
CREATE ROLE leaf_role WITH ADMIN mid_role;
|
||||
CREATE ROLE plain_role;
|
||||
GRANT SELECT ON *.* TO admin_role;
|
||||
GRANT INSERT, UPDATE ON *.* TO mid_role;
|
||||
GRANT SHOW DATABASES ON *.* TO leaf_role;
|
||||
GRANT PROCESS ON *.* TO plain_role;
|
||||
GRANT leaf_role TO test_user@localhost WITH ADMIN OPTION;
|
||||
GRANT plain_role TO test_user@localhost WITH ADMIN OPTION;
|
||||
SET DEFAULT ROLE leaf_role FOR test_user@localhost;
|
||||
|
||||
--exec $MYSQL_DUMP --system=users --replace --skip-dump-date > $MYSQLTEST_VARDIR/tmp/dump_roles.sql
|
||||
|
||||
DROP USER test_user@localhost;
|
||||
DROP ROLE plain_role;
|
||||
DROP ROLE leaf_role;
|
||||
DROP ROLE mid_role;
|
||||
DROP ROLE admin_role;
|
||||
|
||||
# The dump grants PROXY ON ''@'%', which needs PROXY admin on the importer.
|
||||
CREATE USER test_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO test_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO test_restore WITH GRANT OPTION;
|
||||
|
||||
--exec $MYSQL --user=test_restore --password=r --show-warnings < $MYSQLTEST_VARDIR/tmp/dump_roles.sql
|
||||
|
||||
--echo # The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='test_restore' OR Role='mariadb_dump_import_role';
|
||||
|
||||
--disable_warnings
|
||||
DROP USER test_restore;
|
||||
--enable_warnings
|
||||
|
||||
--echo # A second dump is identical: users, grants, roles, admin options and
|
||||
--echo # default roles survived.
|
||||
--exec $MYSQL_DUMP --system=users --replace --skip-dump-date > $MYSQLTEST_VARDIR/tmp/dump_roles2.sql
|
||||
--diff_files $MYSQLTEST_VARDIR/tmp/dump_roles.sql $MYSQLTEST_VARDIR/tmp/dump_roles2.sql
|
||||
|
||||
--echo # The restored state is the hierarchy above, so an omission cannot pass:
|
||||
SELECT User, Host, Role, Admin_option FROM mysql.roles_mapping
|
||||
WHERE Role LIKE '%\\_role' ORDER BY Role, User, Host;
|
||||
SELECT User, Host, default_role FROM mysql.user WHERE User='test_user';
|
||||
|
||||
--echo #
|
||||
--echo # --replace import refuses to run as the user it would drop
|
||||
--echo #
|
||||
CREATE USER test_self@localhost IDENTIFIED BY 'pw';
|
||||
CREATE USER test_other@localhost IDENTIFIED BY 'pw';
|
||||
--exec $MYSQL_DUMP --system=users --replace > $MYSQLTEST_VARDIR/tmp/dump_self.sql
|
||||
|
||||
--echo # the whole dump, run by the guarded user: refused by the guard, not
|
||||
--echo # by a DROP USER privilege error
|
||||
--error 1
|
||||
--exec $MYSQL --user=test_self --password=pw < $MYSQLTEST_VARDIR/tmp/dump_self.sql 2> $MYSQLTEST_VARDIR/tmp/guard_self.err
|
||||
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/guard_self.err
|
||||
--let SEARCH_PATTERN= (?:\A|\n)ERROR 30001 \(45000\) at line \d+: Cannot remove the current user during import
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
--perl
|
||||
my $d= "$ENV{MYSQLTEST_VARDIR}/tmp";
|
||||
open(my $in, '<', "$d/dump_self.sql") or die $!;
|
||||
local $/;
|
||||
my $s= <$in>;
|
||||
close $in;
|
||||
$s =~ /(DELIMITER \|\n\/\*M!100101 IF current_user\(\)='test_self\@localhost' THEN.*?END IF \*\/\|\nDELIMITER ;\n)/s
|
||||
or die "guard not found";
|
||||
open(my $out, '>', "$d/guard_self.sql") or die $!;
|
||||
print $out $1;
|
||||
close $out;
|
||||
EOF
|
||||
|
||||
--echo # the guard alone, run by the guarded user: refused
|
||||
--error 1
|
||||
--exec $MYSQL --user=test_self --password=pw < $MYSQLTEST_VARDIR/tmp/guard_self.sql 2> $MYSQLTEST_VARDIR/tmp/guard_self.err
|
||||
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/guard_self.err
|
||||
--let SEARCH_PATTERN= (?:\A|\n)ERROR 30001 \(45000\) at line \d+: Cannot remove the current user during import
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
--echo # any other user: passes
|
||||
--exec $MYSQL --user=test_other --password=pw < $MYSQLTEST_VARDIR/tmp/guard_self.sql
|
||||
|
||||
DROP USER test_self@localhost;
|
||||
DROP USER test_other@localhost;
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_self.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/guard_self.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/guard_self.err
|
||||
|
||||
--echo #
|
||||
--echo # A name with a comment end is split over string literals in an
|
||||
--echo # EXECUTE IMMEDIATE, not emitted inside a /*M!...*/ comment.
|
||||
--echo #
|
||||
CREATE USER test_ss@localhost IDENTIFIED BY 'pw';
|
||||
CREATE USER `u_ss*/x`@localhost IDENTIFIED BY 'pw';
|
||||
CREATE ROLE `r_ss*/x`;
|
||||
CREATE ROLE r_admin;
|
||||
GRANT SELECT ON *.* TO `r_ss*/x`;
|
||||
GRANT `r_ss*/x` TO r_admin WITH ADMIN OPTION;
|
||||
GRANT `r_ss*/x` TO test_ss@localhost WITH ADMIN OPTION;
|
||||
--echo # also with a newline, a quote and a backslash
|
||||
CREATE ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
|
||||
--let $ssdump = $MYSQLTEST_VARDIR/tmp/dump_starslash.sql
|
||||
--exec $MYSQL_DUMP --system=users --replace --skip-dump-date > $ssdump
|
||||
--let SEARCH_FILE = $ssdump
|
||||
|
||||
--echo # no version comment line holds a comment end before its own (expect NOT FOUND):
|
||||
--let SEARCH_PATTERN = (?:\A|\n)/\*M![^\n]*\*/[^\n]*\*/
|
||||
--let SEARCH_ABORT = FOUND
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
--echo # the role and the user are emitted split (expect FOUND):
|
||||
--let SEARCH_ABORT = NOT FOUND
|
||||
--let SEARCH_PATTERN = EXECUTE IMMEDIATE CONCAT\('CREATE OR REPLACE ROLE ''r_ss\*', '/x''
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN = EXECUTE IMMEDIATE CONCAT\('DROP USER IF EXISTS [^\n]*u_ss\*', '/x
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN = current_user\(\)=CONCAT\('u_ss\*', '/x@localhost'\)
|
||||
--source include/search_pattern_in_file.inc
|
||||
|
||||
--echo # the dump disables account auto-creation (expect FOUND):
|
||||
--let SEARCH_PATTERN = sql_mode=CONCAT_WS\(',', NULLIF\(\@\@sql_mode, ''\), 'NO_AUTO_CREATE_USER'\)
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_ABORT =
|
||||
|
||||
--echo # the dump imports and recreates the names intact:
|
||||
DROP USER test_ss@localhost;
|
||||
DROP USER `u_ss*/x`@localhost;
|
||||
DROP ROLE `r_ss*/x`;
|
||||
DROP ROLE r_admin;
|
||||
DROP ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
CREATE USER test_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO test_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO test_restore WITH GRANT OPTION;
|
||||
--exec $MYSQL --user=test_restore --password=r < $ssdump
|
||||
--disable_warnings
|
||||
DROP USER test_restore;
|
||||
--enable_warnings
|
||||
SELECT user AS User, host AS Host, is_role FROM mysql.user
|
||||
WHERE user LIKE 'u\_ss%' OR user LIKE 'r\_ss%' OR user LIKE 'r\_nl%'
|
||||
ORDER BY user;
|
||||
SHOW GRANTS FOR `r_ss*/x`;
|
||||
SHOW GRANTS FOR test_ss@localhost;
|
||||
DROP USER test_ss@localhost;
|
||||
DROP USER `u_ss*/x`@localhost;
|
||||
DROP ROLE `r_ss*/x`;
|
||||
DROP ROLE r_admin;
|
||||
DROP ROLE `r_nl*/
|
||||
INJECTED_NL_MARKER'\`;
|
||||
--remove_file $ssdump
|
||||
|
||||
--echo #
|
||||
--echo # A server whose version string has no "MariaDB" is still a MariaDB
|
||||
--echo # source: roles, their grants and default roles are dumped
|
||||
--echo #
|
||||
--let $restart_parameters= --version=8.0.40
|
||||
--source include/restart_mysqld.inc
|
||||
CREATE ROLE r_ver;
|
||||
CREATE USER u_ver@localhost;
|
||||
GRANT SELECT ON *.* TO r_ver;
|
||||
GRANT r_ver TO u_ver@localhost;
|
||||
SET DEFAULT ROLE r_ver FOR u_ver@localhost;
|
||||
|
||||
--let $verdump= $MYSQLTEST_VARDIR/tmp/dump_version.sql
|
||||
--exec $MYSQL_DUMP --system=users --skip-dump-date > $verdump
|
||||
--let SEARCH_FILE= $verdump
|
||||
--let SEARCH_ABORT= NOT FOUND
|
||||
--let SEARCH_PATTERN= ROLE 'r_ver'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= GRANT `r_ver` TO `u_ver`@`localhost`
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= SET DEFAULT ROLE 'r_ver' FOR 'u_ver'@'localhost'
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_ABORT=
|
||||
|
||||
--echo # a table named mysql.role_edges does not make it look like MySQL 8:
|
||||
--echo # the dump is the same
|
||||
CREATE TABLE mysql.role_edges (FROM_HOST CHAR(255));
|
||||
--exec $MYSQL_DUMP --system=users --skip-dump-date > $MYSQLTEST_VARDIR/tmp/dump_version_edges.sql
|
||||
DROP TABLE mysql.role_edges;
|
||||
--diff_files $verdump $MYSQLTEST_VARDIR/tmp/dump_version_edges.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_version_edges.sql
|
||||
|
||||
DROP USER u_ver@localhost;
|
||||
DROP ROLE r_ver;
|
||||
--remove_file $verdump
|
||||
--let $restart_parameters=
|
||||
--source include/restart_mysqld.inc
|
||||
|
||||
--source include/restore_priv_tables.inc
|
||||
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_roles.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_roles2.sql
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
@@ -67,30 +67,28 @@ SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;
|
||||
CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;
|
||||
GRANT mariadb_dump_import_role TO CURRENT_USER();
|
||||
SET ROLE mariadb_dump_import_role;
|
||||
/*!80001 CREATE ROLE 'role_1' */;
|
||||
/*M!100005 SET @mariadb_dump_saved_sql_mode=@@sql_mode,
|
||||
sql_mode=CONCAT_WS(',', NULLIF(@@sql_mode, ''), 'NO_AUTO_CREATE_USER') */;
|
||||
/*M!100005 CREATE ROLE 'role_1' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_1' TO 'root'@'localhost' WITH ADMIN OPTION */;
|
||||
/*!80001 CREATE ROLE 'role_2' */;
|
||||
/*M!100005 CREATE ROLE 'role_2' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_2' TO 'role_1' WITH ADMIN OPTION */;
|
||||
GRANT USAGE ON *.* TO `mariadb.sys`@`localhost`;
|
||||
GRANT SELECT, DELETE ON `mysql`.`global_priv` TO `mariadb.sys`@`localhost`;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'mariadb.sys'@'localhost' */;
|
||||
/*!80001 ALTER USER 'mariadb.sys'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `root`@`localhost` WITH ADMIN OPTION;
|
||||
GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION;
|
||||
GRANT PROXY ON ``@`%` TO `root`@`localhost` WITH GRANT OPTION;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'root'@'localhost' */;
|
||||
/*!80001 ALTER USER 'root'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `foobar`@`%`;
|
||||
GRANT `role_2` TO `foobar`@`%`;
|
||||
GRANT USAGE ON *.* TO `foobar`@`%` IDENTIFIED VIA test_plugin_server USING 'plug_dest';
|
||||
/*M!100005 SET DEFAULT ROLE 'role_2' FOR 'foobar'@'%' */;
|
||||
/*!80001 ALTER USER 'foobar'@'%' DEFAULT ROLE 'role_2' */;
|
||||
GRANT `role_2` TO `role_1` WITH ADMIN OPTION;
|
||||
GRANT SHOW DATABASES ON *.* TO `role_1`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
/*M!100005 SET sql_mode=@mariadb_dump_saved_sql_mode */;
|
||||
SET ROLE NONE;
|
||||
DROP ROLE mariadb_dump_import_role;
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;
|
||||
@@ -665,56 +663,52 @@ INSTALL PLUGIN `test_plugin_server` SONAME 'AUTH_TEST_PLUGIN_LIB';
|
||||
/*M!100401 UNINSTALL PLUGIN IF EXIST `cleartext_plugin_server` */;
|
||||
INSTALL PLUGIN `cleartext_plugin_server` SONAME 'AUTH_TEST_PLUGIN_LIB';
|
||||
DELIMITER |
|
||||
/*M!100101 IF current_user()='''mariadb.sys''@''localhost''' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT="Don't remove current user 'mariadb.sys'@'localhost''";
|
||||
/*M!100101 IF current_user()='mariadb.sys@localhost' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT='Cannot remove the current user during import';
|
||||
END IF */|
|
||||
DELIMITER ;
|
||||
/*!50701 DROP USER IF EXISTS 'mariadb.sys'@'localhost' */;
|
||||
DELIMITER |
|
||||
/*M!100101 IF current_user()='root@localhost' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT='Cannot remove the current user during import';
|
||||
END IF */|
|
||||
DELIMITER ;
|
||||
DELIMITER |
|
||||
/*M!100101 IF current_user()='foobar@%' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT='Cannot remove the current user during import';
|
||||
END IF */|
|
||||
DELIMITER ;
|
||||
/*M!100103 DROP USER IF EXISTS 'mariadb.sys'@'localhost' */;
|
||||
CREATE /*M!100103 OR REPLACE */ USER `mariadb.sys`@`localhost` PASSWORD EXPIRE;
|
||||
DELIMITER |
|
||||
/*M!100101 IF current_user()='''root''@''localhost''' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT="Don't remove current user 'root'@'localhost''";
|
||||
END IF */|
|
||||
DELIMITER ;
|
||||
/*!50701 DROP USER IF EXISTS 'root'@'localhost' */;
|
||||
/*M!100103 DROP USER IF EXISTS 'root'@'localhost' */;
|
||||
CREATE /*M!100103 OR REPLACE */ USER `root`@`localhost`;
|
||||
DELIMITER |
|
||||
/*M!100101 IF current_user()='''foobar''@''%''' THEN
|
||||
SIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=30001, MESSAGE_TEXT="Don't remove current user 'foobar'@'%''";
|
||||
END IF */|
|
||||
DELIMITER ;
|
||||
/*!50701 DROP USER IF EXISTS 'foobar'@'%' */;
|
||||
/*M!100103 DROP USER IF EXISTS 'foobar'@'%' */;
|
||||
CREATE /*M!100103 OR REPLACE */ USER `foobar`@`%` IDENTIFIED VIA test_plugin_server USING 'plug_dest';
|
||||
SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;
|
||||
CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;
|
||||
GRANT mariadb_dump_import_role TO CURRENT_USER();
|
||||
SET ROLE mariadb_dump_import_role;
|
||||
/*!80001 DROP ROLE IF EXISTS 'role_1' */;
|
||||
/*!80001 CREATE ROLE 'role_1' */;
|
||||
/*M!100005 SET @mariadb_dump_saved_sql_mode=@@sql_mode,
|
||||
sql_mode=CONCAT_WS(',', NULLIF(@@sql_mode, ''), 'NO_AUTO_CREATE_USER') */;
|
||||
/*M!100103 CREATE OR REPLACE ROLE 'role_1' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_1' TO 'root'@'localhost' WITH ADMIN OPTION */;
|
||||
/*!80001 DROP ROLE IF EXISTS 'role_2' */;
|
||||
/*!80001 CREATE ROLE 'role_2' */;
|
||||
/*M!100103 CREATE OR REPLACE ROLE 'role_2' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_2' TO 'role_1' WITH ADMIN OPTION */;
|
||||
GRANT USAGE ON *.* TO `mariadb.sys`@`localhost`;
|
||||
GRANT SELECT, DELETE ON `mysql`.`global_priv` TO `mariadb.sys`@`localhost`;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'mariadb.sys'@'localhost' */;
|
||||
/*!80001 ALTER USER 'mariadb.sys'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `root`@`localhost` WITH ADMIN OPTION;
|
||||
GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION;
|
||||
GRANT PROXY ON ``@`%` TO `root`@`localhost` WITH GRANT OPTION;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'root'@'localhost' */;
|
||||
/*!80001 ALTER USER 'root'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `foobar`@`%`;
|
||||
GRANT `role_2` TO `foobar`@`%`;
|
||||
GRANT USAGE ON *.* TO `foobar`@`%` IDENTIFIED VIA test_plugin_server USING 'plug_dest';
|
||||
/*M!100005 SET DEFAULT ROLE 'role_2' FOR 'foobar'@'%' */;
|
||||
/*!80001 ALTER USER 'foobar'@'%' DEFAULT ROLE 'role_2' */;
|
||||
GRANT `role_2` TO `role_1` WITH ADMIN OPTION;
|
||||
GRANT SHOW DATABASES ON *.* TO `role_1`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
/*M!100005 SET sql_mode=@mariadb_dump_saved_sql_mode */;
|
||||
SET ROLE NONE;
|
||||
DROP ROLE mariadb_dump_import_role;
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;
|
||||
@@ -1295,30 +1289,28 @@ SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;
|
||||
CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;
|
||||
GRANT mariadb_dump_import_role TO CURRENT_USER();
|
||||
SET ROLE mariadb_dump_import_role;
|
||||
/*!80001 CREATE ROLE IF NOT EXISTS 'role_1' */;
|
||||
/*M!100005 SET @mariadb_dump_saved_sql_mode=@@sql_mode,
|
||||
sql_mode=CONCAT_WS(',', NULLIF(@@sql_mode, ''), 'NO_AUTO_CREATE_USER') */;
|
||||
/*M!100005 CREATE ROLE IF NOT EXISTS 'role_1' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_1' TO 'root'@'localhost' WITH ADMIN OPTION */;
|
||||
/*!80001 CREATE ROLE IF NOT EXISTS 'role_2' */;
|
||||
/*M!100005 CREATE ROLE IF NOT EXISTS 'role_2' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'role_2' TO 'role_1' WITH ADMIN OPTION */;
|
||||
GRANT USAGE ON *.* TO `mariadb.sys`@`localhost`;
|
||||
GRANT SELECT, DELETE ON `mysql`.`global_priv` TO `mariadb.sys`@`localhost`;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'mariadb.sys'@'localhost' */;
|
||||
/*!80001 ALTER USER 'mariadb.sys'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `root`@`localhost` WITH ADMIN OPTION;
|
||||
GRANT ALL PRIVILEGES ON *.* TO `root`@`localhost` WITH GRANT OPTION;
|
||||
GRANT PROXY ON ``@`%` TO `root`@`localhost` WITH GRANT OPTION;
|
||||
/*M!100005 SET DEFAULT ROLE NONE FOR 'root'@'localhost' */;
|
||||
/*!80001 ALTER USER 'root'@'localhost' DEFAULT ROLE NONE */;
|
||||
GRANT `role_1` TO `foobar`@`%`;
|
||||
GRANT `role_2` TO `foobar`@`%`;
|
||||
GRANT USAGE ON *.* TO `foobar`@`%` IDENTIFIED VIA test_plugin_server USING 'plug_dest';
|
||||
/*M!100005 SET DEFAULT ROLE 'role_2' FOR 'foobar'@'%' */;
|
||||
/*!80001 ALTER USER 'foobar'@'%' DEFAULT ROLE 'role_2' */;
|
||||
GRANT `role_2` TO `role_1` WITH ADMIN OPTION;
|
||||
GRANT SHOW DATABASES ON *.* TO `role_1`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
GRANT USAGE ON *.* TO `role_2`;
|
||||
/*M!100005 SET sql_mode=@mariadb_dump_saved_sql_mode */;
|
||||
SET ROLE NONE;
|
||||
DROP ROLE mariadb_dump_import_role;
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;
|
||||
@@ -1873,6 +1865,12 @@ SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS;
|
||||
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION;
|
||||
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
|
||||
|
||||
#
|
||||
# --insert-ignore takes precedence over --replace
|
||||
#
|
||||
FOUND 1 /--replace is ignored, --insert-ignore takes precedence/ in dump_both.err
|
||||
NOT FOUND /OR REPLACE|DROP USER IF EXISTS/ in dump_both.sql
|
||||
FOUND 3 /CREATE USER IF NOT EXISTS/ in dump_both.sql
|
||||
SELECT * FROM mysql.global_priv ORDER BY User,Host;
|
||||
Host User Priv
|
||||
% foobar {"access":0,"version_id":VERSION,"plugin":"test_plugin_server","authentication_string":"plug_dest","password_last_changed":NOW,"default_role":"role_2"}
|
||||
|
||||
@@ -102,6 +102,27 @@ eval CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "$UDF_EXAMPLE_SO";
|
||||
--exec $MYSQL_DUMP --skip-comments --system=all --insert-ignore --no-autocommit=0
|
||||
|
||||
|
||||
--echo #
|
||||
--echo # --insert-ignore takes precedence over --replace
|
||||
--echo #
|
||||
|
||||
--exec $MYSQL_DUMP --system=users --replace --insert-ignore > $MYSQLTEST_VARDIR/tmp/dump_both.sql 2> $MYSQLTEST_VARDIR/tmp/dump_both.err
|
||||
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/dump_both.err
|
||||
--let SEARCH_PATTERN= --replace is ignored, --insert-ignore takes precedence
|
||||
--let SEARCH_ABORT= NOT FOUND
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_FILE= $MYSQLTEST_VARDIR/tmp/dump_both.sql
|
||||
--let SEARCH_PATTERN= OR REPLACE|DROP USER IF EXISTS
|
||||
--let SEARCH_ABORT= FOUND
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN= CREATE USER IF NOT EXISTS
|
||||
--let SEARCH_ABORT= NOT FOUND
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_ABORT=
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_both.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/dump_both.err
|
||||
|
||||
|
||||
# global_priv checksum not restored because:
|
||||
# mariadb.sys - different Priv on restore
|
||||
# password_last_changed date isn't saved/restored
|
||||
|
||||
399
mysql-test/std_data/mysql8_users_dump.sql
Normal file
399
mysql-test/std_data/mysql8_users_dump.sql
Normal file
@@ -0,0 +1,399 @@
|
||||
/*M!999999\- enable the sandbox mode */
|
||||
-- MariaDB dump 10.20-13.1.2-MariaDB, for Linux (x86_64)
|
||||
--
|
||||
-- Host: localhost Database:
|
||||
-- ------------------------------------------------------
|
||||
-- Server version 26.7.0
|
||||
|
||||
SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT;
|
||||
SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS;
|
||||
SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION;
|
||||
SET NAMES utf8mb4;
|
||||
SET @OLD_TIME_ZONE=@@TIME_ZONE;
|
||||
SET TIME_ZONE='+00:00';
|
||||
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
|
||||
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
|
||||
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO';
|
||||
/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;
|
||||
CREATE USER ``@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$FMZ_]I-tzZU`aByEVhT9V.2uIloqRkcMJ2PLmibTmHlCw.WJKzP1d0XIsUyC' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `O'Brien`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\nr]azkIep0uR[4M4sWmbRdn4cGlrRQfNrec/vBIzJN9PZr1j0kSlYFa6n9' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8 edge space`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\\0\"Ge}&C[nC3F)LcO9zzpJA8bRXRYBBeXyWW8Uf2gJkPLkcKNCMRTSCN2' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_all_privs`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$sm{lZs z^7O(;p;I0xEPdBny1g5uL23Dvq72X8mb/SR6DPKvrO70zAtM7r4' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_auth_caching_sha2`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$ bK#i8E,\\\n~F8+8cnnN53cmw8R1WcCV.P.tgX88EPff9bLmFmZAmeQdE1E.' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_auth_no_pwd`@`%` IDENTIFIED WITH 'caching_sha2_password' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_auth_sha256`@`%` REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
# WARNING: auth plugin 'sha256_password' not shipped with MariaDB; user created with no password and ACCOUNT LOCK - set a password and ACCOUNT UNLOCK before login
|
||||
CREATE USER `m8_col_priv`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$ki#x2L1E;rLI\Z+O=e15MzJ0iz872j3s0jdbKLAgjLlqkrH.ecWWVVBXfsr6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_combined`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A${c2PCf~,XEgD>t6X3wiSe61Dyv61/fZNbBPcnIjggVlbJQb5lRMFq3QkXD' REQUIRE SSL WITH MAX_USER_CONNECTIONS 5 PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
# WARNING: password policy clause dropped (no MariaDB equivalent): PASSWORD HISTORY 3
|
||||
# WARNING: password policy clause dropped (no MariaDB equivalent): PASSWORD REUSE INTERVAL 90 DAY
|
||||
CREATE USER `m8_db_underscore`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$-A.N\nR9l2/##X59^4OpQdgFtCcPrmDkfWJBStpsm9vreIQGrwrBkxBnaLlP1' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_default_role_many`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$ff4>%G\ZD3HlJWR-yH4yQR.CnWQV8R79qyZr0xqjbOgQVjZcgrJGCFoauSd6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_default_role_one`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$?0o0\\ m/YN~8YmDJsaqwLeA2YxTA/yTixyhgEwXTGWwZomyUBEzfQtK1' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dual_member`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$WhDj\"9 Q|]=Z~gn0hU1l8HRvRaqpNso4AB4MSyznFLYw9Xgt5u7BGUWHi5' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dumper`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$+Z#c8Exf%79UFZfK4WoaLF.lZnlJNz80pT57y.gvoq0rrZTZK2n9T/NWXmA' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_all_dropped`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$!o~2qV{<\\R7+iUmDv9u0c0q5sQwvTGusxqWyL5JTSkXwj5L84M9sq0IHi9' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_flush_collapse`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$d o7l#&VO{x`J!vywu9DM5pGXbuF3SxdnVvhdngbxX2EcNvilmSH3gxWPx0' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_grant_opt`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$.((\ZK:u<e0q9dDs:QHChMVxQpRRvGD7HSFLCqcIcAD03V3HLVAE.XKZRGr5' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_mapped`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$p@k+AU!&*=*0tvUdUCopD3fQ2tXBkD9Hp3iRdY.PMVFr6cWXbKj7WTe86dHW321' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_mixed`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$71r2SI&\nCs\\L115m&NChqNpnkdldmI/nE97F5dGrztgsA113RGIuCfkf1ZH2' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_dyn_unmapped`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$tc3\r\"fGW79\"oA7:rJuj/VNZ0Akrj4X7.YYoSPXNegGArpzFVGVqZz398u/' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_edge@in_name`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$6im%Mv97t;3aO\rTM/l97fzgpCbQj8je/abW65A66j8fzbyTNBP10e4QCD' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_failed_login`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$ %\n: k!6:(JNjei!tlLjb8N1.b2ZLo/Ki7WZUl0JjgbBRezn73qm68NI71mk9' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: account option dropped (no MariaDB equivalent): FAILED_LOGIN_ATTEMPTS 3
|
||||
# WARNING: account option dropped (no MariaDB equivalent): PASSWORD_LOCK_TIME 1
|
||||
CREATE USER `m8_func_exec`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$@}]8H-)4oAeP[%X.XnuQaFYDDXuXTKfIVD/xbt3Ajakcz4CqCIopEDxeLW2' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_host_dual`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$O|]fSqUTj\r eze\"E\ZRmgsR3/Ew98bghKeyh0p/QrNlOsV3zsvBQ3LKNMcbH1' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_hybrid`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$ag8F\n3iFzGE`}@\rK.TLJxjQ2uVEOaXCfQJuqUJms2YPB/DSAkaHojkCCL6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_hybrid_member`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$s)X\ZjKKj9Jlv)ey7x;6I2ucvDYyQveLLVtfjxvtItWZPMcKOYsFg4HFvTuRjD' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_lock`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$>4OwIJN2E;f`:sDlCu1rFclAFr2CVuRwmYsaREcvkFdm0U0frjtjxnz6a/D' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
CREATE USER `m8_max_conn`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$hk8>W1LbDEg\nT~Oj8zc.LcdjiksX0UIsKeLA2Ci/Fn8jdvasTdxDPyWK7' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 1000 MAX_USER_CONNECTIONS 10 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_multiscope`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$t|QRAI!/\rh!%M4yZ/6HEN7qH7BpaTl2yAGAPiqFXOchxXiMD5Z.5WED8' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_partial_revoke`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$>K)`\ZdZIL{VV?A6AJZD.n1YA4YWsCjbGrvkL/jxRC1UffRvb.9oxXleb0U4PXq5' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_proc_exec`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$%B=GRz|.3c6m{1hfSGCRKK9gnYCrbrDInH.Qqi5/hjuogTxWE7iPfpVCX6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_proxy_target`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$()|t7(9=_M Lt3h6XrPDiNrsiv.V5ZhJwD7GW8qL6Bd6t7gpnqcjDmhdeLM8' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_proxy_user`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$xcU>DBq@{3#v]SK*FBwCry4uUTT3aWvUGh.Xkj/EbuofXVQ23m7w1AoRV16' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_pwd_expire_default`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$z\rY7xW >HI:%f]#JZn5aO8BLTjHvAaJnpiaxkHXEjUA0XFwEOLuEsjNHLD' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_pwd_expire_interval`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$v3/a)ypB#Jd41@)~f/sL6x3xF.tSokS5Wk9SVf9NZrBvwYH9VmyV7O4PJi3' REQUIRE NONE PASSWORD EXPIRE INTERVAL 90 DAY ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_pwd_expire_never`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\"m\Z)-Z\r]jBy1@@0weKTln4AVlA5X72fk7jFwT9MDbbzvnf2W0LbiWUhO8' REQUIRE NONE PASSWORD EXPIRE NEVER ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_pwd_expire_now`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$~=sT((x%?^v[Y<zxDi4.LVnQODsa/orH4fSsPrD94uRM5tJLVaLWKBLa6' REQUIRE NONE PASSWORD EXPIRE ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_pwd_history`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$7Ck~VZZ{a%\\unbDSfSizjHdGUt3ab4k7achn7SOlAe/BxjY4Buxby/' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: password policy clause dropped (no MariaDB equivalent): PASSWORD HISTORY 5
|
||||
CREATE USER `m8_pwd_require_current`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$9uc@H;Ns(LHfB>r99VPB6ONFNBEuO4lLmTmRuJuVD5aE/HF49/GP9m.7C7B' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: password policy clause dropped (no MariaDB equivalent): PASSWORD REQUIRE CURRENT
|
||||
CREATE USER `m8_pwd_reuse`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$&Rn2+\rj-E&(U!i*)RAkCw22jcEIKbS63YXhdcL00GTGktWnbnwMWG7Ieq7wy1' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: password policy clause dropped (no MariaDB equivalent): PASSWORD REUSE INTERVAL 365 DAY
|
||||
CREATE USER `m8_require_cipher`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$Bp^`;g>Zr.iDGN%a41CUDgnyFyE08rgGtPLRpP/Ww7AYKQptoh8EA2vSQvz5' REQUIRE CIPHER 'ECDHE-RSA-AES128-GCM-SHA256' PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_require_ssl`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$:a*b:3D{U%D&`0&4we2mV10v0AVS3JsiCX2XmjnWGoioUH7WMlesx2n3q6' REQUIRE SSL PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_require_x509`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\"C7>ju \rkm^Kuas/xnCJOdRCSFKowK9vpHmLhltD358ekbJaGQUeBvz1.D' REQUIRE X509 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_resource_full`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$CB!+FMRX|(3\\p\'&HnhC4rx4H2RIOkKgTRseBY05kdzXJ4eCU.JGEqUvPU/' REQUIRE NONE WITH MAX_QUERIES_PER_HOUR 5000 MAX_UPDATES_PER_HOUR 500 MAX_CONNECTIONS_PER_HOUR 100 MAX_USER_CONNECTIONS 25 PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_role_static`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$|p>j!V3Nhdo}ef\\tFjQ3RhbYSwDSl1F6iflWCwrF5yuQJkF33FT8yfsJ1A' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_routine_admin`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$J{9ndjP%,Cl:Z&6dN4M1T/hkxnyUVYMXVKzbidJr1sXNsDaU1R9ctxt84mpA' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_shared_user_1`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$MxoQ1k66olY\nQ\nS\Z0Rjk4mHBaJst.0LilpIcQPRCITyodrcyx2Yp/wpfBub6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_shared_user_2`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$^c1,6779\"7?PN+~j:jAfMmIQonXwgSbmY4ywjy.yilJPZeO5FN/kQj0TAn71' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_shared_user_3`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$Q7v0M#KW{rL\'1>YSvdrvl8fT7pkQvl8/zzoZi6gel16N5jBJ1OQjMIcEA17' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_shared_user_4`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$Z%P#Ie0)}%(F| eFHjalj9Iyl/zVROajvKNWvm.hV.cLqhDBw.fqKV9kkC' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_shared_user_5`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$!smtjJTSp8Nn/VLtR.bfPuLz0EqdTJNVrXcS14HLrOWjs0i3JPY8kX.8' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_simple`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$hE%IhaKxmz(yiB\rM3Y1nNQqNKeHJy8Ddq7OkdaRZloD/QeK4HnwwZWpHtV/A' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_starslash_comment`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$lX,\" r-?)SP%ft<KJUCr8LlLTkCRxQpNHhJ8opVmYbIr7oFytz09/v5WsufKd6D' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: account option dropped (no MariaDB equivalent): ATTRIBUTE '{"comment": "has */ in body"}'
|
||||
CREATE USER `m8_starslash_escaped`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$66|codaYU(l6%\\IfnBTSx706tDilo.Jvn5WmIuk6N0DgvZg99FlW8WLsB' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: account option dropped (no MariaDB equivalent): ATTRIBUTE '{"comment": "has \\\\*/ escaped"}'
|
||||
CREATE USER `m8_static_dyn_mix`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$Tqk\Z/O/;W4Hc5%-6 C2rFSbGeKkVKJEIuwxq1YvwmhJ3/h0kPVMBQ9qxEOSA' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_static_priv`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\rQp]#W`4G]W\rDiBFkUgR0Mc4eAL0yct4n9Sn/TMhkeockulHSGrub./VL2' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_static_role_mix`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$B!lF1.EiZ7b`Kq1qT9GfH6WKuR5rHaGsyDOF5.aaXqo8s0hQ5u5g.VYxFHG0' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_system_grants`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$f^!!Kg\n\'jF]STki*9QZCfgOrAsTexBsJRAgzAe9WFpTJk//Q7Wnqt0ST3h8' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_unlock`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$}4yUuc:I]?mpo:F8S75fUwwwHk2pK6jJ.DSO69e.1kLVSheAm.yEi04W4' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_usage_only`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$1Njb\Z\Z:C%aNl::xX1kqOziTMqFcv8/UJxFW7B56gJONavujWd258hsTaB' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_with_attribute`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$\rmnC\\%|<dz5\\f .aR/UrmzYP1oavLKzB8gYZXHCMvJ02O8q0Q951JMGea6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: account option dropped (no MariaDB equivalent): ATTRIBUTE '{"team": "db_migration", "contact": "alice@example.com"}'
|
||||
CREATE USER `m8_with_comment`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$6%7GnA!,U2QN U7msxvzSQAqDGquRIFdceT65rHPoEimCzBVG151tbX/5' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
# WARNING: account option dropped (no MariaDB equivalent): ATTRIBUTE '{"comment": "Migration test user with embedded \'quote\' character"}'
|
||||
CREATE USER `m8_用户`@`%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$G7q[N8 Fo&=iR%/17cGeC.6z9lTnN2U3QbZZqyX7ro7G78G61KbaatQD.' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_edge_wildcard_host`@`192.168.%.%` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$~\rJS^(vjRB3!!SN1QzHwlbLrF5R4dWfO2/zKtZwXIaBXEIqHmJdZunR6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER ``@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$0)v@fc]_Ci\\_W\Z~%O_lmIYm90LWjFS4OpWE/XWlR7aHEpJHzRRg1zju4pS1i1' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_host_dual`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$Xf5C5g24,=\ZzqTMJQqND8nhIcQ5JGW7ClJ2tzl9tYw2SQgR1LEXwt6' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `m8_host_localhost`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$00A$dmSE}E/UZWOBHeeE9bEaxAi2Nl/gBeaMhYKVgp01nFxqLFulDnEal4wkvcB' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
CREATE USER `mysql.infoschema`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
CREATE USER `mysql.session`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
CREATE USER `mysql.sys`@`localhost` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;
|
||||
CREATE USER `root`@`localhost` IDENTIFIED WITH 'caching_sha2_password' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
|
||||
SELECT COALESCE(CURRENT_ROLE(),'NONE') into @current_role;
|
||||
CREATE ROLE IF NOT EXISTS mariadb_dump_import_role;
|
||||
GRANT mariadb_dump_import_role TO CURRENT_USER();
|
||||
SET ROLE mariadb_dump_import_role;
|
||||
/*M!100005 SET @mariadb_dump_saved_sql_mode=@@sql_mode,
|
||||
sql_mode=CONCAT_WS(',', NULLIF(@@sql_mode, ''), 'NO_AUTO_CREATE_USER') */;
|
||||
# WARNING: MySQL account 'm8_admin_chain_root' (hosts '%') is locked, passwordless, expired and granted to nobody; dumped as CREATE ROLE (MySQL 8 has no role flag), recreate it as a user if it was a disabled account
|
||||
/*M!100005 CREATE ROLE 'm8_admin_chain_root' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_default_a' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_default_b' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_hybrid' WITH ADMIN mariadb_dump_import_role */;
|
||||
# WARNING: MySQL account 'm8_role*/starslash' (hosts '%') is locked, passwordless, expired and granted to nobody; dumped as CREATE ROLE (MySQL 8 has no role flag), recreate it as a user if it was a disabled account
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('CREATE ROLE ''m8_role*', '/starslash'' WITH ADMIN mariadb_dump_import_role') */;
|
||||
/*M!100005 CREATE ROLE 'm8_role_a' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_role_b' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_role_c' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_role_d' WITH ADMIN mariadb_dump_import_role */;
|
||||
# WARNING: 2 MySQL roles named 'm8_role_dual' on different hosts ('h1','h2') map onto one hostless MariaDB role; they are merged
|
||||
/*M!100005 CREATE ROLE 'm8_role_dual' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 CREATE ROLE 'm8_shared_role' WITH ADMIN mariadb_dump_import_role */;
|
||||
/*M!100005 GRANT 'm8_default_a' TO 'm8_combined'@'%' */;
|
||||
/*M!100005 GRANT 'm8_default_a' TO 'm8_default_role_many'@'%' */;
|
||||
/*M!100005 GRANT 'm8_default_a' TO 'm8_default_role_one'@'%' */;
|
||||
/*M!100005 GRANT 'm8_default_a' TO 'm8_shared_user_1'@'%' */;
|
||||
/*M!100005 GRANT 'm8_default_a' TO 'm8_shared_user_2'@'%' */;
|
||||
/*M!100005 GRANT 'm8_default_b' TO 'm8_default_role_many'@'%' */;
|
||||
/*M!100005 GRANT 'm8_hybrid' TO 'm8_hybrid_member'@'%' */;
|
||||
/*M!100005 GRANT 'm8_role_a' TO 'm8_admin_chain_root' WITH ADMIN OPTION */;
|
||||
/*M!100005 GRANT 'm8_role_a' TO 'm8_simple'@'%' WITH ADMIN OPTION */;
|
||||
/*M!100005 GRANT 'm8_role_b' TO 'm8_role_a' WITH ADMIN OPTION */;
|
||||
/*M!100005 GRANT 'm8_role_c' TO 'm8_hybrid' */;
|
||||
/*M!100005 GRANT 'm8_role_c' TO 'm8_hybrid'@'%' */;
|
||||
# WARNING: role 'm8_role_c' is granted to 'm8_orphan_member'@'%', which is not in mysql.user; the grant is skipped
|
||||
/*M!100005 GRANT 'm8_role_c' TO 'm8_role_b' WITH ADMIN OPTION */;
|
||||
/*M!100005 GRANT 'm8_role_d' TO 'm8_simple'@'%' */;
|
||||
/*M!100005 GRANT 'm8_role_dual' TO 'm8_dual_member'@'%' */;
|
||||
/*M!100005 GRANT 'm8_shared_role' TO 'm8_shared_user_1'@'%' */;
|
||||
/*M!100005 GRANT 'm8_shared_role' TO 'm8_shared_user_2'@'%' WITH ADMIN OPTION */;
|
||||
/*M!100005 GRANT 'm8_shared_role' TO 'm8_shared_user_3'@'%' */;
|
||||
/*M!100005 GRANT 'm8_shared_role' TO 'm8_shared_user_4'@'%' */;
|
||||
/*M!100005 GRANT 'm8_shared_role' TO 'm8_shared_user_5'@'%' WITH ADMIN OPTION */;
|
||||
# WARNING: mandatory_roles is m8_role_c: these roles are granted to every user implicitly and MariaDB has no equivalent; grant them explicitly if needed
|
||||
GRANT USAGE ON *.* TO ``@`%`;
|
||||
GRANT USAGE ON *.* TO ``@`localhost`;
|
||||
GRANT USAGE ON *.* TO `O'Brien`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `O'Brien`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8 edge space`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8 edge space`@`%`;
|
||||
# WARNING: dropped MySQL privilege CREATE ROLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege DROP ROLE (no MariaDB equivalent)
|
||||
/*M!100005 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE ON *.* TO `m8_all_privs`@`%` WITH GRANT OPTION */;
|
||||
# WARNING: dropped MySQL privilege ALLOW_NONEXISTENT_DEFINER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege APPLICATION_PASSWORD_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUDIT_ABORT_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUDIT_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUTHENTICATION_POLICY_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BINLOG_ENCRYPTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CLONE_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CREATE_SPATIAL_REFERENCE_SYSTEM (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege ENCRYPTION_KEY_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FIREWALL_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_OPTIMIZER_COSTS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_PRIVILEGES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_STATUS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_TABLES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_USER_RESOURCES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege GROUP_REPLICATION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege GROUP_REPLICATION_STREAM (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege INNODB_REDO_LOG_ARCHIVE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege INNODB_REDO_LOG_ENABLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege MANAGE_DATA_MASKING_POLICY (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege OPTIMIZE_LOCAL_TABLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege PASSWORDLESS_USER_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege PERSIST_RO_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege RESOURCE_GROUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege RESOURCE_GROUP_USER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege ROLE_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SENSITIVE_VARIABLES_OBSERVER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SERVICE_CONNECTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SESSION_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_USER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TABLE_ENCRYPTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TELEMETRY_LOG_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TRANSACTION_GTID_TAG (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege XA_RECOVER_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: WITH GRANT OPTION dropped from a translated privilege: MariaDB would make all global privileges of the account grantable
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN, CONNECTION ADMIN, REPLICATION SLAVE ADMIN, SET USER, SHOW CREATE ROUTINE ON *.* TO `m8_all_privs`@`%` */;
|
||||
GRANT USAGE ON *.* TO `m8_auth_caching_sha2`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_auth_no_pwd`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_auth_no_pwd`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_auth_sha256`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_col_priv`@`%`;
|
||||
GRANT SELECT (`col_a`, `col_underscore_b`), UPDATE (`col_a`) ON `m8_db`.`tab` TO `m8_col_priv`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_combined`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_combined`@`%`;
|
||||
/*M!100005 SET DEFAULT ROLE 'm8_default_a' FOR 'm8_combined'@'%' */;
|
||||
GRANT USAGE ON *.* TO `m8_db_underscore`@`%`;
|
||||
GRANT SELECT ON `m8_under_score_db`.* TO `m8_db_underscore`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_default_role_many`@`%`;
|
||||
/*M!100005 SET DEFAULT ROLE 'm8_default_a' FOR 'm8_default_role_many'@'%' */;
|
||||
# WARNING: 'm8_default_role_many'@'%' has multiple default roles in the source; MariaDB supports a single default role, dropping 'm8_default_b'
|
||||
GRANT USAGE ON *.* TO `m8_default_role_one`@`%`;
|
||||
/*M!100005 SET DEFAULT ROLE 'm8_default_a' FOR 'm8_default_role_one'@'%' */;
|
||||
GRANT USAGE ON *.* TO `m8_dual_member`@`%`;
|
||||
GRANT SELECT, RELOAD, PROCESS, SHOW DATABASES, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, CREATE USER, EVENT, TRIGGER ON *.* TO `m8_dumper`@`%`;
|
||||
GRANT SELECT ON `mysql`.* TO `m8_dumper`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_dyn_all_dropped`@`%`;
|
||||
# WARNING: dropped MySQL privilege AUDIT_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CLONE_ADMIN (no MariaDB equivalent)
|
||||
GRANT USAGE ON *.* TO `m8_dyn_flush_collapse`@`%`;
|
||||
# WARNING: dropped MySQL privilege FLUSH_OPTIMIZER_COSTS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_STATUS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_TABLES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_USER_RESOURCES (no MariaDB equivalent)
|
||||
GRANT USAGE ON *.* TO `m8_dyn_grant_opt`@`%`;
|
||||
# WARNING: WITH GRANT OPTION dropped from a translated privilege: MariaDB would make all global privileges of the account grantable
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN, CONNECTION ADMIN ON *.* TO `m8_dyn_grant_opt`@`%` */;
|
||||
GRANT USAGE ON *.* TO `m8_dyn_mapped`@`%`;
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN ON *.* TO `m8_dyn_mapped`@`%` */;
|
||||
GRANT USAGE ON *.* TO `m8_dyn_mixed`@`%`;
|
||||
# WARNING: dropped MySQL privilege AUDIT_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN, CONNECTION ADMIN, SHOW CREATE ROUTINE ON *.* TO `m8_dyn_mixed`@`%` */;
|
||||
GRANT USAGE ON *.* TO `m8_dyn_unmapped`@`%`;
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
GRANT USAGE ON *.* TO `m8_edge@in_name`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_edge@in_name`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_edge_wildcard_host`@`192.168.%.%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_edge_wildcard_host`@`192.168.%.%`;
|
||||
GRANT USAGE ON *.* TO `m8_failed_login`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_func_exec`@`%`;
|
||||
GRANT EXECUTE ON FUNCTION `m8_db`.`func_one` TO `m8_func_exec`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_host_dual`@`%`;
|
||||
GRANT SELECT ON `m8_db`.`tab2` TO `m8_host_dual`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_host_dual`@`localhost`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_host_dual`@`localhost`;
|
||||
GRANT USAGE ON *.* TO `m8_host_localhost`@`localhost`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_host_localhost`@`localhost`;
|
||||
GRANT USAGE ON *.* TO `m8_hybrid`@`%`;
|
||||
GRANT SELECT ON `m8_db`.`tab` TO `m8_hybrid`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_hybrid_member`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_lock`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_max_conn`@`%`;
|
||||
GRANT PROCESS ON *.* TO `m8_multiscope`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_multiscope`@`%`;
|
||||
GRANT INSERT, UPDATE ON `m8_db`.`tab` TO `m8_multiscope`@`%`;
|
||||
GRANT SELECT, INSERT ON *.* TO `m8_partial_revoke`@`%`;
|
||||
# WARNING: partial REVOKE has no MariaDB equivalent, ignored: REVOKE INSERT ON `m8_db`.* FROM `m8_partial_revoke`@`%`
|
||||
# the wider GRANT remains in effect; re-grant at the narrower scope if needed.
|
||||
GRANT USAGE ON *.* TO `m8_proc_exec`@`%`;
|
||||
GRANT EXECUTE ON PROCEDURE `m8_db`.`proc_one` TO `m8_proc_exec`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_proxy_target`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_proxy_user`@`%`;
|
||||
GRANT PROXY ON `m8_proxy_target`@`%` TO `m8_proxy_user`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_expire_default`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_expire_interval`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_expire_never`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_expire_now`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_history`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_require_current`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_pwd_reuse`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_require_cipher`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_require_ssl`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_require_x509`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_resource_full`@`%`;
|
||||
# WARNING: dropped MySQL privilege CREATE ROLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege DROP ROLE (no MariaDB equivalent)
|
||||
GRANT USAGE ON *.* TO `m8_routine_admin`@`%`;
|
||||
GRANT EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `m8_db`.* TO `m8_routine_admin`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_shared_user_1`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_shared_user_2`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_shared_user_3`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_shared_user_4`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_shared_user_5`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_simple`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_simple`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_starslash_comment`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_starslash_escaped`@`%`;
|
||||
GRANT SELECT ON *.* TO `m8_static_dyn_mix`@`%`;
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN ON *.* TO `m8_static_dyn_mix`@`%` */;
|
||||
GRANT RELOAD, PROCESS ON *.* TO `m8_static_priv`@`%`;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON `m8_db`.* TO `m8_static_priv`@`%`;
|
||||
# WARNING: dropped MySQL privilege CREATE ROLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege DROP ROLE (no MariaDB equivalent)
|
||||
/*M!100005 GRANT SELECT, INSERT ON *.* TO `m8_static_role_mix`@`%` */;
|
||||
GRANT USAGE ON *.* TO `m8_system_grants`@`%`;
|
||||
GRANT SELECT ON `performance_schema`.* TO `m8_system_grants`@`%`;
|
||||
GRANT SELECT ON `mysql`.`user` TO `m8_system_grants`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_unlock`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_usage_only`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_with_attribute`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_with_comment`@`%`;
|
||||
GRANT USAGE ON *.* TO `m8_用户`@`%`;
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_用户`@`%`;
|
||||
GRANT SELECT ON *.* TO `mysql.infoschema`@`localhost`;
|
||||
# WARNING: dropped MySQL privilege AUDIT_ABORT_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FIREWALL_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_USER (no MariaDB equivalent)
|
||||
GRANT SHUTDOWN, SUPER ON *.* TO `mysql.session`@`localhost`;
|
||||
# WARNING: dropped MySQL privilege AUDIT_ABORT_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUTHENTICATION_POLICY_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CLONE_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FIREWALL_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege PERSIST_RO_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SESSION_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_USER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
/*M!100005 GRANT CONNECTION ADMIN ON *.* TO `mysql.session`@`localhost` */;
|
||||
GRANT SELECT ON `performance_schema`.* TO `mysql.session`@`localhost`;
|
||||
GRANT SELECT ON `mysql`.`user` TO `mysql.session`@`localhost`;
|
||||
GRANT USAGE ON *.* TO `mysql.sys`@`localhost`;
|
||||
# WARNING: dropped MySQL privilege AUDIT_ABORT_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FIREWALL_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_USER (no MariaDB equivalent)
|
||||
GRANT TRIGGER ON `sys`.* TO `mysql.sys`@`localhost`;
|
||||
GRANT SELECT ON `sys`.`sys_config` TO `mysql.sys`@`localhost`;
|
||||
# WARNING: dropped MySQL privilege CREATE ROLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege DROP ROLE (no MariaDB equivalent)
|
||||
/*M!100005 GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE ON *.* TO `root`@`localhost` WITH GRANT OPTION */;
|
||||
# WARNING: dropped MySQL privilege ALLOW_NONEXISTENT_DEFINER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege APPLICATION_PASSWORD_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUDIT_ABORT_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUDIT_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege AUTHENTICATION_POLICY_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege BINLOG_ENCRYPTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CLONE_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege CREATE_SPATIAL_REFERENCE_SYSTEM (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege ENCRYPTION_KEY_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FIREWALL_EXEMPT (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_OPTIMIZER_COSTS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_PRIVILEGES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_STATUS (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_TABLES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege FLUSH_USER_RESOURCES (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege GROUP_REPLICATION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege GROUP_REPLICATION_STREAM (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege INNODB_REDO_LOG_ARCHIVE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege INNODB_REDO_LOG_ENABLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege MANAGE_DATA_MASKING_POLICY (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege OPTIMIZE_LOCAL_TABLE (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege PASSWORDLESS_USER_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege PERSIST_RO_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege RESOURCE_GROUP_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege RESOURCE_GROUP_USER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege ROLE_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SENSITIVE_VARIABLES_OBSERVER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SERVICE_CONNECTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SESSION_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_USER (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege SYSTEM_VARIABLES_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TABLE_ENCRYPTION_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TELEMETRY_LOG_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege TRANSACTION_GTID_TAG (no MariaDB equivalent)
|
||||
# WARNING: dropped MySQL privilege XA_RECOVER_ADMIN (no MariaDB equivalent)
|
||||
# WARNING: WITH GRANT OPTION dropped from a translated privilege: MariaDB would make all global privileges of the account grantable
|
||||
/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN, CONNECTION ADMIN, REPLICATION SLAVE ADMIN, SET USER, SHOW CREATE ROUTINE ON *.* TO `root`@`localhost` */;
|
||||
GRANT PROXY ON ``@`` TO `root`@`localhost` WITH GRANT OPTION;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_admin_chain_root' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_default_a' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_default_b' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_hybrid' */;
|
||||
/*M!100005 GRANT SELECT ON `m8_db`.`tab` TO 'm8_hybrid' */;
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('GRANT USAGE ON *.* TO ''m8_role*', '/starslash''') */;
|
||||
# WARNING: dropped MySQL privilege BACKUP_ADMIN (no MariaDB equivalent)
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_role_a' */;
|
||||
/*M!100005 GRANT SELECT ON `m8_db`.* TO 'm8_role_a' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_role_b' */;
|
||||
/*M!100005 GRANT INSERT ON `m8_db`.* TO 'm8_role_b' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_role_c' */;
|
||||
/*M!100005 GRANT UPDATE ON `m8_db`.* TO 'm8_role_c' */;
|
||||
/*M!100005 GRANT SHOW DATABASES ON *.* TO 'm8_role_d' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_role_dual' */;
|
||||
/*M!100005 GRANT SELECT ON `m8_db`.`tab` TO 'm8_role_dual' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_role_dual' */;
|
||||
/*M!100005 GRANT INSERT ON `m8_db`.`tab` TO 'm8_role_dual' */;
|
||||
/*M!100005 GRANT USAGE ON *.* TO 'm8_shared_role' */;
|
||||
/*M!100005 GRANT SELECT ON `m8_db`.`tab` TO 'm8_shared_role' */;
|
||||
/*M!100005 GRANT EXECUTE ON PROCEDURE `m8_db`.`proc_one` TO 'm8_shared_role' */;
|
||||
/*M!100005 SET sql_mode=@mariadb_dump_saved_sql_mode */;
|
||||
SET ROLE NONE;
|
||||
DROP ROLE mariadb_dump_import_role;
|
||||
/*M!100203 EXECUTE IMMEDIATE CONCAT('SET ROLE ', @current_role) */;
|
||||
SET TIME_ZONE=@OLD_TIME_ZONE;
|
||||
|
||||
SET SQL_MODE=@OLD_SQL_MODE;
|
||||
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
|
||||
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
|
||||
SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT;
|
||||
SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS;
|
||||
SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION;
|
||||
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
|
||||
|
||||
-- Dump completed on 2026-10-20 17:29:06
|
||||
419
mysql-test/std_data/mysql8_users_setup.sql
Normal file
419
mysql-test/std_data/mysql8_users_setup.sql
Normal file
@@ -0,0 +1,419 @@
|
||||
-- =====================================================================
|
||||
-- Test fixture: MySQL 8.x state for round-trip testing of mariadb-dump.
|
||||
--
|
||||
-- Workflow:
|
||||
-- 1. mysql -uroot -p < mysql8_users_setup.sql (on a fresh MySQL 8.x)
|
||||
-- 2. ./client/mariadb-dump --plugin-dir=$BUILD/libmariadb \
|
||||
-- -h <host> -P <port> -u root --system=users > mysql8_users_dump.sql
|
||||
-- 3. Commit the captured dump.
|
||||
--
|
||||
-- Step 1 needs a SUPER user (or SYSTEM_VARIABLES_ADMIN): Section 18 sets
|
||||
-- partial_revokes. The committed capture was taken as root; m8_dumper of
|
||||
-- Section 2 has the privileges a non-root dump needs.
|
||||
--
|
||||
-- Names describe the case they exercise (m8_dyn_mapped, ...) so a failing
|
||||
-- import test points at it. Re-runs are safe (drops everything first).
|
||||
-- COMMENT/ATTRIBUTE need MySQL 8.0.21+, FAILED_LOGIN_ATTEMPTS 8.0.19+.
|
||||
-- =====================================================================
|
||||
|
||||
-- Section 0: cleanup
|
||||
SET GLOBAL mandatory_roles = '';
|
||||
DELETE FROM mysql.role_edges WHERE TO_USER = 'm8_orphan_member';
|
||||
DROP USER IF EXISTS
|
||||
'm8_dumper'@'%',
|
||||
'm8_simple'@'%',
|
||||
'm8_static_priv'@'%',
|
||||
'm8_dyn_mapped'@'%',
|
||||
'm8_dyn_unmapped'@'%',
|
||||
'm8_dyn_mixed'@'%',
|
||||
'm8_dyn_all_dropped'@'%',
|
||||
'm8_static_dyn_mix'@'%',
|
||||
'm8_role_static'@'%',
|
||||
'm8_static_role_mix'@'%',
|
||||
'm8_dyn_grant_opt'@'%',
|
||||
'm8_dyn_flush_collapse'@'%',
|
||||
'm8_col_priv'@'%',
|
||||
'm8_db_underscore'@'%',
|
||||
'm8 edge space'@'%',
|
||||
'm8_edge@in_name'@'%',
|
||||
'm8_edge_wildcard_host'@'192.168.%.%',
|
||||
'm8_all_privs'@'%',
|
||||
'm8_multiscope'@'%',
|
||||
'm8_proc_exec'@'%',
|
||||
'm8_func_exec'@'%',
|
||||
'm8_routine_admin'@'%',
|
||||
'm8_proxy_target'@'%',
|
||||
'm8_proxy_user'@'%',
|
||||
'm8_usage_only'@'%',
|
||||
'm8_lock'@'%',
|
||||
'm8_unlock'@'%',
|
||||
'm8_default_role_one'@'%',
|
||||
'm8_default_role_many'@'%',
|
||||
'm8_pwd_history'@'%',
|
||||
'm8_pwd_reuse'@'%',
|
||||
'm8_pwd_require_current'@'%',
|
||||
'm8_pwd_expire_now'@'%',
|
||||
'm8_pwd_expire_never'@'%',
|
||||
'm8_pwd_expire_interval'@'%',
|
||||
'm8_pwd_expire_default'@'%',
|
||||
'm8_max_conn'@'%',
|
||||
'm8_resource_full'@'%',
|
||||
'm8_require_ssl'@'%',
|
||||
'm8_require_x509'@'%',
|
||||
'm8_require_cipher'@'%',
|
||||
'm8_failed_login'@'%',
|
||||
'm8_with_comment'@'%',
|
||||
'm8_with_attribute'@'%',
|
||||
'm8_combined'@'%',
|
||||
'm8_shared_user_1'@'%',
|
||||
'm8_shared_user_2'@'%',
|
||||
'm8_shared_user_3'@'%',
|
||||
'm8_shared_user_4'@'%',
|
||||
'm8_shared_user_5'@'%',
|
||||
'm8_auth_sha256'@'%',
|
||||
'm8_auth_no_pwd'@'%',
|
||||
'm8_auth_caching_sha2'@'%',
|
||||
'm8_host_localhost'@'localhost',
|
||||
'm8_host_dual'@'localhost',
|
||||
'm8_host_dual'@'%',
|
||||
''@'localhost',
|
||||
''@'%',
|
||||
'O\'Brien'@'%',
|
||||
'm8_用户'@'%',
|
||||
'm8_starslash_comment'@'%',
|
||||
'm8_starslash_escaped'@'%',
|
||||
'm8_partial_revoke'@'%',
|
||||
'm8_system_grants'@'%',
|
||||
'm8_hybrid'@'%',
|
||||
'm8_hybrid_member'@'%',
|
||||
'm8_dual_member'@'%';
|
||||
|
||||
DROP ROLE IF EXISTS
|
||||
'm8_role_a','m8_role_b','m8_role_c','m8_role_d',
|
||||
'm8_default_a','m8_default_b',
|
||||
'm8_shared_role','m8_admin_chain_root',
|
||||
`m8_role*/starslash`,
|
||||
'm8_role_dual'@'h1','m8_role_dual'@'h2';
|
||||
|
||||
DROP DATABASE IF EXISTS m8_db;
|
||||
DROP DATABASE IF EXISTS `m8_under_score_db`;
|
||||
|
||||
-- Section 1: schemas, tables, routines
|
||||
CREATE DATABASE m8_db;
|
||||
CREATE TABLE m8_db.tab (col_a INT, col_underscore_b INT, plain INT);
|
||||
CREATE TABLE m8_db.tab2 (id INT PRIMARY KEY, payload TEXT);
|
||||
CREATE VIEW m8_db.v AS SELECT col_a FROM m8_db.tab;
|
||||
|
||||
DELIMITER $$
|
||||
CREATE PROCEDURE m8_db.proc_one(IN x INT) BEGIN SELECT x; END$$
|
||||
CREATE FUNCTION m8_db.func_one(x INT) RETURNS INT DETERMINISTIC
|
||||
BEGIN RETURN x * 2; END$$
|
||||
DELIMITER ;
|
||||
|
||||
CREATE DATABASE `m8_under_score_db`;
|
||||
CREATE TABLE `m8_under_score_db`.t (a INT);
|
||||
|
||||
-- Section 2: dumping user
|
||||
CREATE USER 'm8_dumper'@'%' IDENTIFIED BY 'dumper_pw';
|
||||
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER,
|
||||
RELOAD, PROCESS, REPLICATION CLIENT, SHOW DATABASES, CREATE USER
|
||||
ON *.* TO 'm8_dumper'@'%';
|
||||
GRANT SELECT ON mysql.* TO 'm8_dumper'@'%';
|
||||
|
||||
-- Section 3: roles — 3-level admin chain + a 5-user-shared role
|
||||
CREATE ROLE 'm8_role_a','m8_role_b','m8_role_c','m8_role_d';
|
||||
CREATE ROLE 'm8_default_a','m8_default_b';
|
||||
CREATE ROLE 'm8_shared_role','m8_admin_chain_root';
|
||||
|
||||
GRANT 'm8_role_b' TO 'm8_role_a' WITH ADMIN OPTION;
|
||||
GRANT 'm8_role_c' TO 'm8_role_b' WITH ADMIN OPTION;
|
||||
GRANT SELECT ON m8_db.* TO 'm8_role_a';
|
||||
GRANT INSERT ON m8_db.* TO 'm8_role_b';
|
||||
GRANT UPDATE ON m8_db.* TO 'm8_role_c';
|
||||
GRANT SHOW DATABASES ON *.* TO 'm8_role_d';
|
||||
GRANT 'm8_role_a' TO 'm8_admin_chain_root' WITH ADMIN OPTION;
|
||||
GRANT SELECT ON m8_db.tab TO 'm8_shared_role';
|
||||
GRANT EXECUTE ON PROCEDURE m8_db.proc_one TO 'm8_shared_role';
|
||||
|
||||
-- Orphan role with a literal '*/' in its name; its CREATE ROLE is emitted
|
||||
-- split over string literals. Its BACKUP_ADMIN grant is unmapped, so it is
|
||||
-- dropped with a # WARNING.
|
||||
CREATE ROLE `m8_role*/starslash`;
|
||||
GRANT BACKUP_ADMIN ON *.* TO `m8_role*/starslash`;
|
||||
|
||||
-- Section 4: GRANT translator coverage
|
||||
|
||||
-- 4.1 plain SELECT only -> translator NOT triggered
|
||||
CREATE USER 'm8_simple'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_simple'@'%';
|
||||
|
||||
-- 4.2 only static privileges across multiple grants
|
||||
CREATE USER 'm8_static_priv'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON m8_db.* TO 'm8_static_priv'@'%';
|
||||
GRANT RELOAD, PROCESS ON *.* TO 'm8_static_priv'@'%';
|
||||
|
||||
-- 4.3 single dynamic priv WITH MariaDB equivalent
|
||||
CREATE USER 'm8_dyn_mapped'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT BINLOG_ADMIN ON *.* TO 'm8_dyn_mapped'@'%';
|
||||
|
||||
-- 4.4 single dynamic priv with NO MariaDB equivalent -> dropped + warn
|
||||
CREATE USER 'm8_dyn_unmapped'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT BACKUP_ADMIN ON *.* TO 'm8_dyn_unmapped'@'%';
|
||||
|
||||
-- 4.5 mixed mapped + unmapped dynamic privs
|
||||
CREATE USER 'm8_dyn_mixed'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT BACKUP_ADMIN, BINLOG_ADMIN, CONNECTION_ADMIN, AUDIT_ADMIN,
|
||||
REPLICATION_APPLIER, SHOW_ROUTINE
|
||||
ON *.* TO 'm8_dyn_mixed'@'%';
|
||||
|
||||
-- 4.6 all-unmapped -> entire MariaDB GRANT line suppressed
|
||||
CREATE USER 'm8_dyn_all_dropped'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT BACKUP_ADMIN, AUDIT_ADMIN, CLONE_ADMIN ON *.* TO 'm8_dyn_all_dropped'@'%';
|
||||
|
||||
-- 4.7 static + dynamic in one GRANT
|
||||
CREATE USER 'm8_static_dyn_mix'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT, BINLOG_ADMIN, BACKUP_ADMIN ON *.* TO 'm8_static_dyn_mix'@'%';
|
||||
|
||||
-- 4.8 MySQL-only role privs (CREATE ROLE / DROP ROLE)
|
||||
CREATE USER 'm8_role_static'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT CREATE ROLE, DROP ROLE ON *.* TO 'm8_role_static'@'%';
|
||||
|
||||
-- 4.9 CREATE ROLE/DROP ROLE mixed with portable static privs
|
||||
CREATE USER 'm8_static_role_mix'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT, CREATE ROLE, INSERT, DROP ROLE
|
||||
ON *.* TO 'm8_static_role_mix'@'%';
|
||||
|
||||
-- 4.10 WITH GRANT OPTION on dynamic privileges is dropped: MariaDB has one
|
||||
-- grant option per level, which would make every global privilege
|
||||
-- grantable
|
||||
CREATE USER 'm8_dyn_grant_opt'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT BINLOG_ADMIN, CONNECTION_ADMIN ON *.* TO 'm8_dyn_grant_opt'@'%' WITH GRANT OPTION;
|
||||
|
||||
-- 4.11 FLUSH_* family - all dropped + warn (RELOAD would over-grant)
|
||||
CREATE USER 'm8_dyn_flush_collapse'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT FLUSH_OPTIMIZER_COSTS, FLUSH_STATUS, FLUSH_TABLES, FLUSH_USER_RESOURCES
|
||||
ON *.* TO 'm8_dyn_flush_collapse'@'%';
|
||||
|
||||
-- Section 5: identifier-parsing false-positive guards
|
||||
|
||||
CREATE USER 'm8_col_priv'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT(col_a, col_underscore_b), UPDATE(col_a)
|
||||
ON m8_db.tab TO 'm8_col_priv'@'%';
|
||||
|
||||
CREATE USER 'm8_db_underscore'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON `m8_under_score_db`.* TO 'm8_db_underscore'@'%';
|
||||
|
||||
CREATE USER 'm8 edge space'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON m8_db.* TO 'm8 edge space'@'%';
|
||||
|
||||
-- Internal @ must not be treated as host separator.
|
||||
CREATE USER 'm8_edge@in_name'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_edge@in_name'@'%';
|
||||
|
||||
CREATE USER 'm8_edge_wildcard_host'@'192.168.%.%' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_edge_wildcard_host'@'192.168.%.%';
|
||||
|
||||
-- Section 6: scope coverage
|
||||
|
||||
CREATE USER 'm8_all_privs'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT ALL PRIVILEGES ON *.* TO 'm8_all_privs'@'%' WITH GRANT OPTION;
|
||||
|
||||
CREATE USER 'm8_multiscope'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT PROCESS ON *.* TO 'm8_multiscope'@'%';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_multiscope'@'%';
|
||||
GRANT INSERT, UPDATE ON m8_db.tab TO 'm8_multiscope'@'%';
|
||||
GRANT SELECT(col_a) ON m8_db.tab2 TO 'm8_multiscope'@'%';
|
||||
|
||||
-- Section 7: routine privileges
|
||||
CREATE USER 'm8_proc_exec'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT EXECUTE ON PROCEDURE m8_db.proc_one TO 'm8_proc_exec'@'%';
|
||||
|
||||
CREATE USER 'm8_func_exec'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT EXECUTE ON FUNCTION m8_db.func_one TO 'm8_func_exec'@'%';
|
||||
|
||||
CREATE USER 'm8_routine_admin'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT CREATE ROUTINE, ALTER ROUTINE, EXECUTE
|
||||
ON m8_db.* TO 'm8_routine_admin'@'%';
|
||||
|
||||
-- Section 8: PROXY
|
||||
CREATE USER 'm8_proxy_target'@'%' IDENTIFIED BY 'pw';
|
||||
CREATE USER 'm8_proxy_user'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT PROXY ON 'm8_proxy_target'@'%' TO 'm8_proxy_user'@'%';
|
||||
|
||||
-- Section 9: USAGE-only (no privileges granted)
|
||||
CREATE USER 'm8_usage_only'@'%' IDENTIFIED BY 'pw';
|
||||
|
||||
-- Section 10: CREATE USER extension coverage
|
||||
|
||||
CREATE USER 'm8_lock'@'%' IDENTIFIED BY 'pw' ACCOUNT LOCK;
|
||||
CREATE USER 'm8_unlock'@'%' IDENTIFIED BY 'pw' ACCOUNT UNLOCK;
|
||||
|
||||
CREATE USER 'm8_default_role_one'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT 'm8_default_a' TO 'm8_default_role_one'@'%';
|
||||
SET DEFAULT ROLE 'm8_default_a' TO 'm8_default_role_one'@'%';
|
||||
|
||||
CREATE USER 'm8_default_role_many'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT 'm8_default_a','m8_default_b' TO 'm8_default_role_many'@'%';
|
||||
SET DEFAULT ROLE 'm8_default_a','m8_default_b' TO 'm8_default_role_many'@'%';
|
||||
|
||||
CREATE USER 'm8_pwd_history'@'%' IDENTIFIED BY 'pw' PASSWORD HISTORY 5;
|
||||
CREATE USER 'm8_pwd_reuse'@'%' IDENTIFIED BY 'pw' PASSWORD REUSE INTERVAL 365 DAY;
|
||||
CREATE USER 'm8_pwd_require_current'@'%' IDENTIFIED BY 'pw' PASSWORD REQUIRE CURRENT;
|
||||
|
||||
CREATE USER 'm8_pwd_expire_now'@'%' IDENTIFIED BY 'pw' PASSWORD EXPIRE;
|
||||
CREATE USER 'm8_pwd_expire_never'@'%' IDENTIFIED BY 'pw' PASSWORD EXPIRE NEVER;
|
||||
CREATE USER 'm8_pwd_expire_interval'@'%' IDENTIFIED BY 'pw' PASSWORD EXPIRE INTERVAL 90 DAY;
|
||||
CREATE USER 'm8_pwd_expire_default'@'%' IDENTIFIED BY 'pw' PASSWORD EXPIRE DEFAULT;
|
||||
|
||||
CREATE USER 'm8_max_conn'@'%' IDENTIFIED BY 'pw'
|
||||
WITH MAX_USER_CONNECTIONS 10 MAX_QUERIES_PER_HOUR 1000;
|
||||
|
||||
CREATE USER 'm8_resource_full'@'%' IDENTIFIED BY 'pw'
|
||||
WITH MAX_QUERIES_PER_HOUR 5000
|
||||
MAX_UPDATES_PER_HOUR 500
|
||||
MAX_CONNECTIONS_PER_HOUR 100
|
||||
MAX_USER_CONNECTIONS 25;
|
||||
|
||||
CREATE USER 'm8_require_ssl'@'%' IDENTIFIED BY 'pw' REQUIRE SSL;
|
||||
CREATE USER 'm8_require_x509'@'%' IDENTIFIED BY 'pw' REQUIRE X509;
|
||||
CREATE USER 'm8_require_cipher'@'%' IDENTIFIED BY 'pw'
|
||||
REQUIRE CIPHER 'ECDHE-RSA-AES128-GCM-SHA256';
|
||||
|
||||
CREATE USER 'm8_failed_login'@'%' IDENTIFIED BY 'pw'
|
||||
FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 1;
|
||||
|
||||
CREATE USER 'm8_with_comment'@'%' IDENTIFIED BY 'pw'
|
||||
COMMENT 'Migration test user with embedded ''quote'' character';
|
||||
|
||||
CREATE USER 'm8_with_attribute'@'%' IDENTIFIED BY 'pw'
|
||||
ATTRIBUTE '{"team":"db_migration","contact":"alice@example.com"}';
|
||||
|
||||
-- Combined: kept (REQUIRE, WITH MAX_, ACCOUNT LOCK) and dropped
|
||||
-- (DEFAULT ROLE, PASSWORD HISTORY/REUSE) clauses in one CREATE USER.
|
||||
CREATE USER 'm8_combined'@'%' IDENTIFIED BY 'pw'
|
||||
DEFAULT ROLE 'm8_default_a'
|
||||
REQUIRE SSL
|
||||
WITH MAX_USER_CONNECTIONS 5
|
||||
PASSWORD HISTORY 3
|
||||
PASSWORD REUSE INTERVAL 90 DAY
|
||||
ACCOUNT LOCK;
|
||||
GRANT 'm8_default_a' TO 'm8_combined'@'%';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_combined'@'%';
|
||||
|
||||
-- Section 11: dedup stress — five users share m8_shared_role
|
||||
CREATE USER 'm8_shared_user_1'@'%' IDENTIFIED BY 'pw';
|
||||
CREATE USER 'm8_shared_user_2'@'%' IDENTIFIED BY 'pw';
|
||||
CREATE USER 'm8_shared_user_3'@'%' IDENTIFIED BY 'pw';
|
||||
CREATE USER 'm8_shared_user_4'@'%' IDENTIFIED BY 'pw';
|
||||
CREATE USER 'm8_shared_user_5'@'%' IDENTIFIED BY 'pw';
|
||||
GRANT 'm8_shared_role' TO 'm8_shared_user_1'@'%';
|
||||
GRANT 'm8_shared_role' TO 'm8_shared_user_2'@'%' WITH ADMIN OPTION;
|
||||
GRANT 'm8_shared_role' TO 'm8_shared_user_3'@'%';
|
||||
GRANT 'm8_shared_role' TO 'm8_shared_user_4'@'%';
|
||||
GRANT 'm8_shared_role' TO 'm8_shared_user_5'@'%' WITH ADMIN OPTION;
|
||||
|
||||
-- m8_default_a is also handed to two of the shared users, so it too is
|
||||
-- a role held by several users (m8_default_role_one/_many, m8_combined,
|
||||
-- and these two).
|
||||
GRANT 'm8_default_a' TO 'm8_shared_user_1'@'%';
|
||||
GRANT 'm8_default_a' TO 'm8_shared_user_2'@'%';
|
||||
|
||||
-- Section 12: cross-cutting role grants
|
||||
GRANT 'm8_role_a' TO 'm8_simple'@'%' WITH ADMIN OPTION;
|
||||
GRANT 'm8_role_d' TO 'm8_simple'@'%';
|
||||
|
||||
-- Section 13: authentication plugins.
|
||||
-- No mysql_native_password user: disabled in MySQL 8.4, removed in 9.0.
|
||||
-- sha256_password is not shipped by MariaDB: the IDENTIFIED clause is
|
||||
-- dropped with a # WARNING and ACCOUNT LOCK is forced.
|
||||
CREATE USER 'm8_auth_sha256'@'%'
|
||||
IDENTIFIED WITH sha256_password BY 'pw_sha256';
|
||||
CREATE USER 'm8_auth_no_pwd'@'%';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_auth_no_pwd'@'%';
|
||||
-- caching_sha2_password: the IDENTIFIED clause is kept, so the CREATE USER
|
||||
-- fails on import unless the auth_mysql_sha2 plugin is loaded.
|
||||
CREATE USER 'm8_auth_caching_sha2'@'%'
|
||||
IDENTIFIED WITH caching_sha2_password BY 'pw_caching';
|
||||
|
||||
-- Section 14: MFA - INTENTIONALLY OMITTED.
|
||||
-- MySQL 8.4 ER 4052 rejects primary auth plugins as factor 2/3, and
|
||||
-- real MFA plugins (FIDO/Kerberos/LDAP_*) need external infra. The dropped
|
||||
-- extra factors are covered by the unit test (mysql8_translate-t.cc).
|
||||
|
||||
-- Section 15: same name, different hosts (host-aware dedup)
|
||||
CREATE USER 'm8_host_localhost'@'localhost' IDENTIFIED BY 'pw';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_host_localhost'@'localhost';
|
||||
|
||||
CREATE USER 'm8_host_dual'@'localhost' IDENTIFIED BY 'pw_local';
|
||||
CREATE USER 'm8_host_dual'@'%' IDENTIFIED BY 'pw_any';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_host_dual'@'localhost';
|
||||
GRANT SELECT ON m8_db.tab2 TO 'm8_host_dual'@'%';
|
||||
|
||||
-- Section 16: identifier edge cases (anonymous + embedded quote)
|
||||
CREATE USER ''@'localhost' IDENTIFIED BY 'pw_anon';
|
||||
GRANT USAGE ON *.* TO ''@'localhost';
|
||||
|
||||
-- Anonymous-from-anywhere. Real legacy installs sometimes have this.
|
||||
CREATE USER ''@'%' IDENTIFIED BY 'pw_anon_any';
|
||||
GRANT USAGE ON *.* TO ''@'%';
|
||||
|
||||
-- Embedded single-quote in a user name.
|
||||
CREATE USER 'O\'Brien'@'%' IDENTIFIED BY 'pw_obrien';
|
||||
GRANT SELECT ON m8_db.* TO 'O\'Brien'@'%';
|
||||
|
||||
-- UTF-8 / non-ASCII identifier. The translator's parser is byte-level,
|
||||
-- so multibyte chars in identifiers should be transparent.
|
||||
CREATE USER 'm8_用户'@'%' IDENTIFIED BY 'pw_unicode';
|
||||
GRANT SELECT ON m8_db.* TO 'm8_用户'@'%';
|
||||
|
||||
-- COMMENT values with a literal '*/' (plain, and backslash-escaped).
|
||||
-- The dump drops COMMENT with a # WARNING that carries the '*/'.
|
||||
CREATE USER 'm8_starslash_comment'@'%' IDENTIFIED BY 'pw'
|
||||
COMMENT 'has */ in body';
|
||||
|
||||
CREATE USER 'm8_starslash_escaped'@'%' IDENTIFIED BY 'pw'
|
||||
COMMENT 'has \\*/ escaped';
|
||||
|
||||
-- Section 17: system-schema grants. GRANT SELECT ON
|
||||
-- information_schema.* is documented to silently no-op on MySQL
|
||||
-- (I_S is virtual; no privilege row stored), so omitted here.
|
||||
CREATE USER 'm8_system_grants'@'%' IDENTIFIED BY 'pw_sys';
|
||||
GRANT SELECT ON mysql.user TO 'm8_system_grants'@'%';
|
||||
GRANT SELECT ON performance_schema.* TO 'm8_system_grants'@'%';
|
||||
|
||||
-- Section 18: partial REVOKE (covers translate_mysql8_revoke).
|
||||
-- SHOW GRANTS emits a REVOKE line only for a db-level partial revoke of a
|
||||
-- STATIC privilege, with partial_revokes=ON (needs SYSTEM_VARIABLES_ADMIN
|
||||
-- or SUPER). Dynamic privileges are *.* only and make the REVOKE fail.
|
||||
-- Check the dump has a '# WARNING: partial REVOKE' line after recapture.
|
||||
-- Reset afterwards: SET GLOBAL partial_revokes = OFF;
|
||||
SET GLOBAL partial_revokes = ON;
|
||||
CREATE USER 'm8_partial_revoke'@'%' IDENTIFIED BY 'pw_partial';
|
||||
GRANT SELECT, INSERT ON *.* TO 'm8_partial_revoke'@'%';
|
||||
REVOKE INSERT ON `m8_db`.* FROM 'm8_partial_revoke'@'%';
|
||||
|
||||
-- Section 19: hybrid account - a login user that is also granted to another
|
||||
-- account as a role (MySQL has no separate role objects). It must migrate
|
||||
-- as a user with its password and as a role with its grants and members.
|
||||
CREATE USER 'm8_hybrid'@'%' IDENTIFIED BY 'pw_hybrid';
|
||||
GRANT SELECT ON m8_db.tab TO 'm8_hybrid'@'%';
|
||||
GRANT 'm8_role_c' TO 'm8_hybrid'@'%';
|
||||
CREATE USER 'm8_hybrid_member'@'%' IDENTIFIED BY 'pw_member';
|
||||
GRANT 'm8_hybrid'@'%' TO 'm8_hybrid_member'@'%';
|
||||
|
||||
-- Section 20: one role name on two hosts. MariaDB roles have no host, so the
|
||||
-- two must merge into a single role and the shared member is granted once.
|
||||
CREATE ROLE 'm8_role_dual'@'h1', 'm8_role_dual'@'h2';
|
||||
GRANT SELECT ON m8_db.tab TO 'm8_role_dual'@'h1';
|
||||
GRANT INSERT ON m8_db.tab TO 'm8_role_dual'@'h2';
|
||||
CREATE USER 'm8_dual_member'@'%' IDENTIFIED BY 'pw_dual';
|
||||
GRANT 'm8_role_dual'@'h1', 'm8_role_dual'@'h2' TO 'm8_dual_member'@'%';
|
||||
|
||||
-- Section 21: role granted to an account missing from mysql.user (an
|
||||
-- orphaned role_edges row), and a non-empty mandatory_roles. Each makes the
|
||||
-- dump print a warning.
|
||||
CREATE USER 'm8_orphan_member'@'%' IDENTIFIED BY 'pw_orphan';
|
||||
GRANT 'm8_role_c' TO 'm8_orphan_member'@'%';
|
||||
DELETE FROM mysql.user WHERE User = 'm8_orphan_member';
|
||||
SET GLOBAL mandatory_roles = 'm8_role_c';
|
||||
-- Reset after capture: SET GLOBAL mandatory_roles = '';
|
||||
@@ -0,0 +1,3 @@
|
||||
--plugin-load-add=$AUTH_MYSQL_SHA2_SO
|
||||
--loose-caching-sha2-password
|
||||
--loose-disable-caching-sha2-password-auto-generate-rsa-keys
|
||||
@@ -0,0 +1,143 @@
|
||||
# MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: accounts
|
||||
#
|
||||
# Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks CREATE USER clauses and authentication.
|
||||
# Recapture it after changing the MySQL-source code (recipe in
|
||||
# std_data/mysql8_users_setup.sql).
|
||||
SET NAMES utf8mb4;
|
||||
CREATE DATABASE IF NOT EXISTS m8_db;
|
||||
CREATE TABLE m8_db.tab (col_a INT, col_underscore_b INT, plain INT);
|
||||
CREATE TABLE m8_db.tab2 (id INT PRIMARY KEY, payload TEXT);
|
||||
USE m8_db;
|
||||
CREATE PROCEDURE proc_one(IN x INT) BEGIN SELECT x; END$$
|
||||
CREATE FUNCTION func_one(x INT) RETURNS INT DETERMINISTIC BEGIN RETURN x*2; END$$
|
||||
USE test;
|
||||
CREATE DATABASE IF NOT EXISTS `m8_under_score_db`;
|
||||
CALL mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
CALL mtr.add_suppression("Plugin 'caching_sha2_password' is of maturity level gamma");
|
||||
CREATE USER m8_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO m8_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO m8_restore WITH GRANT OPTION;
|
||||
NOT FOUND /(?:\A|\n)ERROR (?!1396 )\d/ in m8_import.err
|
||||
NOT FOUND /(?:\A|\n)ERROR 1396 (?![^\n]*'root'@'localhost')/ in m8_import.err
|
||||
FOUND 1 /sql_mode restored/ in m8_import.out
|
||||
# The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='m8_restore' OR Role='mariadb_dump_import_role';
|
||||
User Role
|
||||
DROP USER m8_restore;
|
||||
# 1. ACCOUNT LOCK / UNLOCK.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.account_locked') AS account_locked
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_lock','m8_unlock')
|
||||
ORDER BY User, Host;
|
||||
User Host account_locked
|
||||
m8_lock % 1
|
||||
m8_unlock % 0
|
||||
# 2. PASSWORD EXPIRE variants.
|
||||
SELECT User, Host,
|
||||
password_expired,
|
||||
JSON_VALUE(Priv, '$.password_lifetime') AS password_lifetime
|
||||
FROM mysql.user
|
||||
JOIN mysql.global_priv USING (User, Host)
|
||||
WHERE User IN ('m8_pwd_expire_now','m8_pwd_expire_never',
|
||||
'm8_pwd_expire_interval','m8_pwd_expire_default')
|
||||
ORDER BY User;
|
||||
User Host password_expired password_lifetime
|
||||
m8_pwd_expire_default % N -1
|
||||
m8_pwd_expire_interval % N 90
|
||||
m8_pwd_expire_never % N 0
|
||||
m8_pwd_expire_now % Y -1
|
||||
# 3. Resource limits.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.max_questions') AS max_q,
|
||||
JSON_VALUE(Priv, '$.max_updates') AS max_u,
|
||||
JSON_VALUE(Priv, '$.max_connections') AS max_c,
|
||||
JSON_VALUE(Priv, '$.max_user_connections') AS max_user_c
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_max_conn','m8_resource_full')
|
||||
ORDER BY User;
|
||||
User Host max_q max_u max_c max_user_c
|
||||
m8_max_conn % 1000 NULL NULL 10
|
||||
m8_resource_full % 5000 500 100 25
|
||||
# 4. REQUIRE variants.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.ssl_type') AS ssl_type,
|
||||
JSON_VALUE(Priv, '$.ssl_cipher') AS ssl_cipher
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_require_ssl','m8_require_x509','m8_require_cipher')
|
||||
ORDER BY User;
|
||||
User Host ssl_type ssl_cipher
|
||||
m8_require_cipher % 3 ECDHE-RSA-AES128-GCM-SHA256
|
||||
m8_require_ssl % 1
|
||||
m8_require_x509 % 2
|
||||
# 5. Combined CREATE USER: kept (REQUIRE, WITH MAX_, ACCOUNT LOCK) and
|
||||
# dropped (DEFAULT ROLE, PASSWORD HISTORY/REUSE) clauses mixed.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.account_locked') AS account_locked,
|
||||
JSON_VALUE(Priv, '$.ssl_type') AS ssl_type,
|
||||
JSON_VALUE(Priv, '$.max_user_connections') AS max_user_c
|
||||
FROM mysql.global_priv
|
||||
WHERE User='m8_combined' AND Host='%';
|
||||
User Host account_locked ssl_type max_user_c
|
||||
m8_combined % 1 1 5
|
||||
SHOW GRANTS FOR 'm8_combined'@'%';
|
||||
Grants for m8_combined@%
|
||||
GRANT `m8_default_a` TO `m8_combined`@`%`
|
||||
GRANT USAGE ON *.* TO `m8_combined`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>' REQUIRE SSL WITH MAX_USER_CONNECTIONS 5
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_combined`@`%`
|
||||
SET DEFAULT ROLE `m8_default_a` FOR `m8_combined`@`%`
|
||||
# 6. Auth plugins: sha256 (dropped, locked), no password, caching_sha2.
|
||||
# mysql_native_password is absent from the fixture (disabled in
|
||||
# MySQL 8.4, removed in 9.0). m8_auth_sha256 covers the unsupported
|
||||
# plugin: the IDENTIFIED clause is dropped with a warning.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.plugin') AS plugin,
|
||||
LENGTH(JSON_VALUE(Priv, '$.authentication_string')) > 0 AS has_auth_string
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_auth_sha256','m8_auth_no_pwd','m8_auth_caching_sha2')
|
||||
ORDER BY User;
|
||||
User Host plugin has_auth_string
|
||||
m8_auth_caching_sha2 % caching_sha2_password 1
|
||||
m8_auth_no_pwd % caching_sha2_password 0
|
||||
m8_auth_sha256 % mysql_native_password 0
|
||||
# m8_auth_sha256 lost its password, so ACCOUNT LOCK was forced.
|
||||
SELECT JSON_VALUE(Priv, '$.account_locked') AS sha256_locked
|
||||
FROM mysql.global_priv WHERE User='m8_auth_sha256';
|
||||
sha256_locked
|
||||
1
|
||||
# 7. COMMENT clause containing '*/' (plain and backslash-escaped).
|
||||
# COMMENT (an ATTRIBUTE in MySQL) goes into a # WARNING line.
|
||||
SELECT User, Host
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_starslash_comment','m8_starslash_escaped')
|
||||
ORDER BY User, Host;
|
||||
User Host
|
||||
m8_starslash_comment %
|
||||
m8_starslash_escaped %
|
||||
# 8. A GRANT to an account that was not created fails instead of
|
||||
# creating a passwordless login.
|
||||
FOUND 1 /(?:\A|\n)ERROR 1133 / in m8_noauto.err
|
||||
SELECT COUNT(*) AS m8_nx_accounts FROM mysql.user WHERE User='m8_nx';
|
||||
m8_nx_accounts
|
||||
0
|
||||
DROP USER ''@'localhost';
|
||||
# caching_sha2_password preservation (Unix only - gated;
|
||||
# 'OK' on Windows means skipped, not exercised).
|
||||
SELECT IF('m8_auth_caching_sha2@%' = 'm8_auth_caching_sha2@%',
|
||||
'OK',
|
||||
CONCAT('FAIL: got ', 'm8_auth_caching_sha2@%')) AS caching_sha2_round_trip;
|
||||
caching_sha2_round_trip
|
||||
OK
|
||||
ALTER USER 'm8_lock'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
# m8_lock: ACCOUNT LOCK survives import; connect rejected.
|
||||
connect(localhost,m8_lock,pw,test,MYSQL_PORT,MYSQL_SOCK);
|
||||
connect c_lock, localhost, m8_lock, pw;
|
||||
ERROR HY000: Access denied, this account is locked
|
||||
SELECT User, Host, JSON_VALUE(Priv, '$.account_locked') AS account_locked
|
||||
FROM mysql.global_priv WHERE User='m8_lock';
|
||||
User Host account_locked
|
||||
m8_lock % 1
|
||||
DROP DATABASE IF EXISTS m8_db;
|
||||
DROP DATABASE IF EXISTS `m8_under_score_db`;
|
||||
# End of 13.1 tests
|
||||
@@ -0,0 +1,135 @@
|
||||
--echo # MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: accounts
|
||||
--echo #
|
||||
--echo # Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks CREATE USER clauses and authentication.
|
||||
--echo # Recapture it after changing the MySQL-source code (recipe in
|
||||
--echo # std_data/mysql8_users_setup.sql).
|
||||
|
||||
--source mariadb-dump-mysql8-import.inc
|
||||
|
||||
--echo # 1. ACCOUNT LOCK / UNLOCK.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.account_locked') AS account_locked
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_lock','m8_unlock')
|
||||
ORDER BY User, Host;
|
||||
|
||||
--echo # 2. PASSWORD EXPIRE variants.
|
||||
SELECT User, Host,
|
||||
password_expired,
|
||||
JSON_VALUE(Priv, '$.password_lifetime') AS password_lifetime
|
||||
FROM mysql.user
|
||||
JOIN mysql.global_priv USING (User, Host)
|
||||
WHERE User IN ('m8_pwd_expire_now','m8_pwd_expire_never',
|
||||
'm8_pwd_expire_interval','m8_pwd_expire_default')
|
||||
ORDER BY User;
|
||||
|
||||
--echo # 3. Resource limits.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.max_questions') AS max_q,
|
||||
JSON_VALUE(Priv, '$.max_updates') AS max_u,
|
||||
JSON_VALUE(Priv, '$.max_connections') AS max_c,
|
||||
JSON_VALUE(Priv, '$.max_user_connections') AS max_user_c
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_max_conn','m8_resource_full')
|
||||
ORDER BY User;
|
||||
|
||||
--echo # 4. REQUIRE variants.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.ssl_type') AS ssl_type,
|
||||
JSON_VALUE(Priv, '$.ssl_cipher') AS ssl_cipher
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_require_ssl','m8_require_x509','m8_require_cipher')
|
||||
ORDER BY User;
|
||||
|
||||
--echo # 5. Combined CREATE USER: kept (REQUIRE, WITH MAX_, ACCOUNT LOCK) and
|
||||
--echo # dropped (DEFAULT ROLE, PASSWORD HISTORY/REUSE) clauses mixed.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.account_locked') AS account_locked,
|
||||
JSON_VALUE(Priv, '$.ssl_type') AS ssl_type,
|
||||
JSON_VALUE(Priv, '$.max_user_connections') AS max_user_c
|
||||
FROM mysql.global_priv
|
||||
WHERE User='m8_combined' AND Host='%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_combined'@'%';
|
||||
|
||||
--echo # 6. Auth plugins: sha256 (dropped, locked), no password, caching_sha2.
|
||||
--echo # mysql_native_password is absent from the fixture (disabled in
|
||||
--echo # MySQL 8.4, removed in 9.0). m8_auth_sha256 covers the unsupported
|
||||
--echo # plugin: the IDENTIFIED clause is dropped with a warning.
|
||||
SELECT User, Host,
|
||||
JSON_VALUE(Priv, '$.plugin') AS plugin,
|
||||
LENGTH(JSON_VALUE(Priv, '$.authentication_string')) > 0 AS has_auth_string
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_auth_sha256','m8_auth_no_pwd','m8_auth_caching_sha2')
|
||||
ORDER BY User;
|
||||
--echo # m8_auth_sha256 lost its password, so ACCOUNT LOCK was forced.
|
||||
SELECT JSON_VALUE(Priv, '$.account_locked') AS sha256_locked
|
||||
FROM mysql.global_priv WHERE User='m8_auth_sha256';
|
||||
|
||||
--echo # 7. COMMENT clause containing '*/' (plain and backslash-escaped).
|
||||
--echo # COMMENT (an ATTRIBUTE in MySQL) goes into a # WARNING line.
|
||||
SELECT User, Host
|
||||
FROM mysql.global_priv
|
||||
WHERE User IN ('m8_starslash_comment','m8_starslash_escaped')
|
||||
ORDER BY User, Host;
|
||||
|
||||
--echo # 8. A GRANT to an account that was not created fails instead of
|
||||
--echo # creating a passwordless login.
|
||||
--perl
|
||||
my $d= "$ENV{MYSQLTEST_VARDIR}";
|
||||
open(my $in, '<', "$d/std_data/mysql8_users_dump.sql") or die $!;
|
||||
local $/;
|
||||
my $s= <$in>;
|
||||
close $in;
|
||||
$s =~ m{(/\*M!100005 SET \@mariadb_dump_saved_sql_mode.*?\*/;\n)}s
|
||||
or die "sql_mode guard not found";
|
||||
open(my $out, '>', "$d/tmp/m8_noauto.sql") or die $!;
|
||||
print $out $1, "GRANT SELECT ON *.* TO m8_nx\@'%';\n";
|
||||
close $out;
|
||||
EOF
|
||||
--error 1
|
||||
--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/m8_noauto.sql 2> $MYSQLTEST_VARDIR/tmp/m8_noauto.err
|
||||
--let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/m8_noauto.err
|
||||
--let SEARCH_PATTERN=(?:\A|\n)ERROR 1133
|
||||
--source include/search_pattern_in_file.inc
|
||||
SELECT COUNT(*) AS m8_nx_accounts FROM mysql.user WHERE User='m8_nx';
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/m8_noauto.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/m8_noauto.err
|
||||
|
||||
# The anonymous localhost user would shadow the m8_*@'%' logins.
|
||||
DROP USER ''@'localhost';
|
||||
|
||||
--echo # caching_sha2_password preservation (Unix only - gated;
|
||||
--echo # 'OK' on Windows means skipped, not exercised).
|
||||
let $is_win = `select convert(@@version_compile_os using latin1) IN ("Win32","Win64","Windows")`;
|
||||
let $cu = m8_auth_caching_sha2@%;
|
||||
if (!$is_win)
|
||||
{
|
||||
--disable_query_log
|
||||
--disable_result_log
|
||||
connect (c_csha, localhost, m8_auth_caching_sha2, pw_caching);
|
||||
let $cu = `SELECT CURRENT_USER()`;
|
||||
disconnect c_csha;
|
||||
connection default;
|
||||
--enable_result_log
|
||||
--enable_query_log
|
||||
}
|
||||
eval SELECT IF('$cu' = 'm8_auth_caching_sha2@%',
|
||||
'OK',
|
||||
CONCAT('FAIL: got ', '$cu')) AS caching_sha2_round_trip;
|
||||
|
||||
# The fixture users have caching_sha2_password, which needs TLS or RSA over TCP.
|
||||
# Switch the ones that connect; the semantics checked do not depend on the plugin.
|
||||
ALTER USER 'm8_lock'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
|
||||
--echo # m8_lock: ACCOUNT LOCK survives import; connect rejected.
|
||||
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
|
||||
--error ER_ACCOUNT_HAS_BEEN_LOCKED
|
||||
connect (c_lock, localhost, m8_lock, pw);
|
||||
|
||||
SELECT User, Host, JSON_VALUE(Priv, '$.account_locked') AS account_locked
|
||||
FROM mysql.global_priv WHERE User='m8_lock';
|
||||
|
||||
--source mariadb-dump-mysql8-import-cleanup.inc
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
@@ -0,0 +1,8 @@
|
||||
DROP DATABASE IF EXISTS m8_db;
|
||||
DROP DATABASE IF EXISTS `m8_under_score_db`;
|
||||
|
||||
--source include/restore_priv_tables.inc
|
||||
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/m8_import.sql
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/m8_import.err
|
||||
--remove_file $MYSQLTEST_VARDIR/tmp/m8_import.out
|
||||
@@ -0,0 +1,3 @@
|
||||
--plugin-load-add=$AUTH_MYSQL_SHA2_SO
|
||||
--loose-caching-sha2-password
|
||||
--loose-disable-caching-sha2-password-auto-generate-rsa-keys
|
||||
@@ -0,0 +1,275 @@
|
||||
# MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: grants
|
||||
#
|
||||
# Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks grants and identifier handling.
|
||||
# Recapture it after changing the MySQL-source code (recipe in
|
||||
# std_data/mysql8_users_setup.sql).
|
||||
SET NAMES utf8mb4;
|
||||
CREATE DATABASE IF NOT EXISTS m8_db;
|
||||
CREATE TABLE m8_db.tab (col_a INT, col_underscore_b INT, plain INT);
|
||||
CREATE TABLE m8_db.tab2 (id INT PRIMARY KEY, payload TEXT);
|
||||
USE m8_db;
|
||||
CREATE PROCEDURE proc_one(IN x INT) BEGIN SELECT x; END$$
|
||||
CREATE FUNCTION func_one(x INT) RETURNS INT DETERMINISTIC BEGIN RETURN x*2; END$$
|
||||
USE test;
|
||||
CREATE DATABASE IF NOT EXISTS `m8_under_score_db`;
|
||||
CALL mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
CALL mtr.add_suppression("Plugin 'caching_sha2_password' is of maturity level gamma");
|
||||
CREATE USER m8_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO m8_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO m8_restore WITH GRANT OPTION;
|
||||
NOT FOUND /(?:\A|\n)ERROR (?!1396 )\d/ in m8_import.err
|
||||
NOT FOUND /(?:\A|\n)ERROR 1396 (?![^\n]*'root'@'localhost')/ in m8_import.err
|
||||
FOUND 1 /sql_mode restored/ in m8_import.out
|
||||
# The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='m8_restore' OR Role='mariadb_dump_import_role';
|
||||
User Role
|
||||
DROP USER m8_restore;
|
||||
# 1. Every m8_* user landed.
|
||||
SELECT user, host FROM mysql.global_priv WHERE user LIKE 'm8\\_%' ORDER BY user, host;
|
||||
user host
|
||||
m8_admin_chain_root
|
||||
m8_all_privs %
|
||||
m8_auth_caching_sha2 %
|
||||
m8_auth_no_pwd %
|
||||
m8_auth_sha256 %
|
||||
m8_col_priv %
|
||||
m8_combined %
|
||||
m8_db_underscore %
|
||||
m8_default_a
|
||||
m8_default_b
|
||||
m8_default_role_many %
|
||||
m8_default_role_one %
|
||||
m8_dual_member %
|
||||
m8_dumper %
|
||||
m8_dyn_all_dropped %
|
||||
m8_dyn_flush_collapse %
|
||||
m8_dyn_grant_opt %
|
||||
m8_dyn_mapped %
|
||||
m8_dyn_mixed %
|
||||
m8_dyn_unmapped %
|
||||
m8_edge@in_name %
|
||||
m8_edge_wildcard_host 192.168.%.%
|
||||
m8_failed_login %
|
||||
m8_func_exec %
|
||||
m8_host_dual %
|
||||
m8_host_dual localhost
|
||||
m8_host_localhost localhost
|
||||
m8_hybrid
|
||||
m8_hybrid %
|
||||
m8_hybrid_member %
|
||||
m8_lock %
|
||||
m8_max_conn %
|
||||
m8_multiscope %
|
||||
m8_partial_revoke %
|
||||
m8_proc_exec %
|
||||
m8_proxy_target %
|
||||
m8_proxy_user %
|
||||
m8_pwd_expire_default %
|
||||
m8_pwd_expire_interval %
|
||||
m8_pwd_expire_never %
|
||||
m8_pwd_expire_now %
|
||||
m8_pwd_history %
|
||||
m8_pwd_require_current %
|
||||
m8_pwd_reuse %
|
||||
m8_require_cipher %
|
||||
m8_require_ssl %
|
||||
m8_require_x509 %
|
||||
m8_resource_full %
|
||||
m8_role*/starslash
|
||||
m8_role_a
|
||||
m8_role_b
|
||||
m8_role_c
|
||||
m8_role_d
|
||||
m8_role_dual
|
||||
m8_role_static %
|
||||
m8_routine_admin %
|
||||
m8_shared_role
|
||||
m8_shared_user_1 %
|
||||
m8_shared_user_2 %
|
||||
m8_shared_user_3 %
|
||||
m8_shared_user_4 %
|
||||
m8_shared_user_5 %
|
||||
m8_simple %
|
||||
m8_starslash_comment %
|
||||
m8_starslash_escaped %
|
||||
m8_static_dyn_mix %
|
||||
m8_static_priv %
|
||||
m8_static_role_mix %
|
||||
m8_system_grants %
|
||||
m8_unlock %
|
||||
m8_usage_only %
|
||||
m8_with_attribute %
|
||||
m8_with_comment %
|
||||
m8_用户 %
|
||||
# 2. Simple static grants pass through unchanged.
|
||||
SHOW GRANTS FOR 'm8_simple'@'%';
|
||||
Grants for m8_simple@%
|
||||
GRANT `m8_role_a` TO `m8_simple`@`%` WITH ADMIN OPTION
|
||||
GRANT `m8_role_d` TO `m8_simple`@`%`
|
||||
GRANT USAGE ON *.* TO `m8_simple`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_simple`@`%`
|
||||
SHOW GRANTS FOR 'm8_static_priv'@'%';
|
||||
Grants for m8_static_priv@%
|
||||
GRANT RELOAD, PROCESS ON *.* TO `m8_static_priv`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON `m8_db`.* TO `m8_static_priv`@`%`
|
||||
# 3. Dynamic priv mapping (BINLOG_ADMIN -> BINLOG REPLAY/ADMIN).
|
||||
SHOW GRANTS FOR 'm8_dyn_mapped'@'%';
|
||||
Grants for m8_dyn_mapped@%
|
||||
GRANT BINLOG ADMIN, BINLOG REPLAY ON *.* TO `m8_dyn_mapped`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 4. Mixed dynamic priv (some mapped, some not).
|
||||
SHOW GRANTS FOR 'm8_dyn_mixed'@'%';
|
||||
Grants for m8_dyn_mixed@%
|
||||
GRANT CONNECTION ADMIN, BINLOG ADMIN, BINLOG REPLAY, SHOW CREATE ROUTINE ON *.* TO `m8_dyn_mixed`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 5. WITH GRANT OPTION of a translated dynamic privilege is dropped.
|
||||
SHOW GRANTS FOR 'm8_dyn_grant_opt'@'%';
|
||||
Grants for m8_dyn_grant_opt@%
|
||||
GRANT CONNECTION ADMIN, BINLOG ADMIN, BINLOG REPLAY ON *.* TO `m8_dyn_grant_opt`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 6. CREATE ROLE / DROP ROLE static privs dropped.
|
||||
SHOW GRANTS FOR 'm8_role_static'@'%';
|
||||
Grants for m8_role_static@%
|
||||
GRANT USAGE ON *.* TO `m8_role_static`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
SHOW GRANTS FOR 'm8_static_role_mix'@'%';
|
||||
Grants for m8_static_role_mix@%
|
||||
GRANT SELECT, INSERT ON *.* TO `m8_static_role_mix`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 7. Identifier parsing: column / db underscores don't false-positive.
|
||||
SHOW GRANTS FOR 'm8_col_priv'@'%';
|
||||
Grants for m8_col_priv@%
|
||||
GRANT USAGE ON *.* TO `m8_col_priv`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT (`col_underscore_b`, `col_a`), UPDATE (`col_a`) ON `m8_db`.`tab` TO `m8_col_priv`@`%`
|
||||
SHOW GRANTS FOR 'm8_db_underscore'@'%';
|
||||
Grants for m8_db_underscore@%
|
||||
GRANT USAGE ON *.* TO `m8_db_underscore`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_under_score_db`.* TO `m8_db_underscore`@`%`
|
||||
# 8. Identifier parsing: edge-case usernames and hostnames.
|
||||
SELECT user, host FROM mysql.global_priv
|
||||
WHERE user IN ('m8 edge space','m8_edge@in_name','m8_edge_wildcard_host')
|
||||
ORDER BY user, host;
|
||||
user host
|
||||
m8 edge space %
|
||||
m8_edge@in_name %
|
||||
m8_edge_wildcard_host 192.168.%.%
|
||||
SHOW GRANTS FOR 'm8_edge_wildcard_host'@'192.168.%.%';
|
||||
Grants for m8_edge_wildcard_host@192.168.%.%
|
||||
GRANT USAGE ON *.* TO `m8_edge_wildcard_host`@`192.168.%.%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_edge_wildcard_host`@`192.168.%.%`
|
||||
# 9. ALL PRIVILEGES expands and survives import.
|
||||
SHOW GRANTS FOR 'm8_all_privs'@'%';
|
||||
Grants for m8_all_privs@%
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, BINLOG MONITOR, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, SET USER, CONNECTION ADMIN, REPLICATION SLAVE ADMIN, BINLOG ADMIN, BINLOG REPLAY, SHOW CREATE ROUTINE ON *.* TO `m8_all_privs`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>' WITH GRANT OPTION
|
||||
# 10. Multi-scope grants for one user.
|
||||
SHOW GRANTS FOR 'm8_multiscope'@'%';
|
||||
Grants for m8_multiscope@%
|
||||
GRANT PROCESS ON *.* TO `m8_multiscope`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_multiscope`@`%`
|
||||
GRANT INSERT, UPDATE ON `m8_db`.`tab` TO `m8_multiscope`@`%`
|
||||
# 11. Routine privileges land in mysql.procs_priv.
|
||||
SELECT User, Host, Db, Routine_name, Routine_type, Proc_priv
|
||||
FROM mysql.procs_priv
|
||||
WHERE User LIKE 'm8\\_%'
|
||||
ORDER BY User, Host, Routine_type, Routine_name;
|
||||
User Host Db Routine_name Routine_type Proc_priv
|
||||
m8_func_exec % m8_db func_one FUNCTION Execute
|
||||
m8_proc_exec % m8_db proc_one PROCEDURE Execute
|
||||
m8_shared_role m8_db proc_one PROCEDURE Execute
|
||||
SHOW GRANTS FOR 'm8_routine_admin'@'%';
|
||||
Grants for m8_routine_admin@%
|
||||
GRANT USAGE ON *.* TO `m8_routine_admin`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT EXECUTE, CREATE ROUTINE, ALTER ROUTINE ON `m8_db`.* TO `m8_routine_admin`@`%`
|
||||
# 12. PROXY grant lands in mysql.proxies_priv.
|
||||
SELECT User, Host, Proxied_user, Proxied_host
|
||||
FROM mysql.proxies_priv
|
||||
WHERE User='m8_proxy_user'
|
||||
ORDER BY User, Host;
|
||||
User Host Proxied_user Proxied_host
|
||||
m8_proxy_user % m8_proxy_target %
|
||||
# 13. USAGE-only user lands with the implicit GRANT USAGE.
|
||||
SHOW GRANTS FOR 'm8_usage_only'@'%';
|
||||
Grants for m8_usage_only@%
|
||||
GRANT USAGE ON *.* TO `m8_usage_only`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 14. System-schema grants (mysql.user, performance_schema.*).
|
||||
SHOW GRANTS FOR 'm8_system_grants'@'%';
|
||||
Grants for m8_system_grants@%
|
||||
GRANT USAGE ON *.* TO `m8_system_grants`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `performance_schema`.* TO `m8_system_grants`@`%`
|
||||
GRANT SELECT ON `mysql`.`user` TO `m8_system_grants`@`%`
|
||||
# 15. Same-name-different-host coverage.
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User IN ('m8_host_localhost','m8_host_dual')
|
||||
ORDER BY User, Host;
|
||||
User Host
|
||||
m8_host_dual %
|
||||
m8_host_dual localhost
|
||||
m8_host_localhost localhost
|
||||
SHOW GRANTS FOR 'm8_host_localhost'@'localhost';
|
||||
Grants for m8_host_localhost@localhost
|
||||
GRANT USAGE ON *.* TO `m8_host_localhost`@`localhost` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_host_localhost`@`localhost`
|
||||
SHOW GRANTS FOR 'm8_host_dual'@'localhost';
|
||||
Grants for m8_host_dual@localhost
|
||||
GRANT USAGE ON *.* TO `m8_host_dual`@`localhost` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_host_dual`@`localhost`
|
||||
SHOW GRANTS FOR 'm8_host_dual'@'%';
|
||||
Grants for m8_host_dual@%
|
||||
GRANT USAGE ON *.* TO `m8_host_dual`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.`tab2` TO `m8_host_dual`@`%`
|
||||
# 16. Identifier edge cases: anonymous + embedded quote + UTF-8.
|
||||
# ORDER BY BINARY for collation-stable byte-level ordering across
|
||||
# servers running different default collations.
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User='' OR User='O\'Brien' OR User='m8_用户'
|
||||
ORDER BY BINARY User, Host;
|
||||
User Host
|
||||
%
|
||||
localhost
|
||||
O'Brien %
|
||||
m8_用户 %
|
||||
SHOW GRANTS FOR 'O\'Brien'@'%';
|
||||
Grants for O'Brien@%
|
||||
GRANT USAGE ON *.* TO `O'Brien`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `O'Brien`@`%`
|
||||
SHOW GRANTS FOR 'm8_用户'@'%';
|
||||
Grants for m8_用户@%
|
||||
GRANT USAGE ON *.* TO `m8_用户`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_用户`@`%`
|
||||
# 17. Partial REVOKE. m8_partial_revoke has a wide GRANT on *.* and a
|
||||
# partial REVOKE on m8_db.*. MariaDB has no equivalent, so only the
|
||||
# wide GRANT lands (the dump carries a # WARNING for the REVOKE).
|
||||
SHOW GRANTS FOR 'm8_partial_revoke'@'%';
|
||||
Grants for m8_partial_revoke@%
|
||||
GRANT SELECT, INSERT ON *.* TO `m8_partial_revoke`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
DROP USER ''@'localhost';
|
||||
ALTER USER 'm8_col_priv'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_role_static'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_proc_exec'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
# m8_col_priv: SELECT/UPDATE on granted columns; non-granted denied.
|
||||
connect c_col, localhost, m8_col_priv, pw, m8_db;
|
||||
SELECT col_a, col_underscore_b FROM m8_db.tab;
|
||||
col_a col_underscore_b
|
||||
SELECT plain FROM m8_db.tab;
|
||||
ERROR 42000: SELECT command denied to user 'm8_col_priv'@'localhost' for column 'plain' in table 'tab'
|
||||
UPDATE m8_db.tab SET col_a = 1 WHERE col_a IS NULL OR col_a IS NOT NULL;
|
||||
UPDATE m8_db.tab SET plain = 1;
|
||||
ERROR 42000: UPDATE command denied to user 'm8_col_priv'@'localhost' for column 'plain' in table 'tab'
|
||||
disconnect c_col;
|
||||
connection default;
|
||||
# m8_role_static landed with USAGE only -> CREATE/DROP ROLE denied.
|
||||
connect c_rs, localhost, m8_role_static, pw;
|
||||
CREATE ROLE m8_should_fail_role;
|
||||
ERROR 42000: Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
|
||||
DROP ROLE IF EXISTS m8_role_a;
|
||||
ERROR 42000: Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
|
||||
disconnect c_rs;
|
||||
connection default;
|
||||
# m8_proc_exec: EXECUTE works; DROP PROCEDURE denied.
|
||||
connect c_proc, localhost, m8_proc_exec, pw, m8_db;
|
||||
CALL m8_db.proc_one(42);
|
||||
x
|
||||
42
|
||||
DROP PROCEDURE m8_db.proc_one;
|
||||
ERROR 42000: alter routine command denied to user 'm8_proc_exec'@'%' for routine 'm8_db.proc_one'
|
||||
disconnect c_proc;
|
||||
connection default;
|
||||
DROP DATABASE IF EXISTS m8_db;
|
||||
DROP DATABASE IF EXISTS `m8_under_score_db`;
|
||||
# End of 13.1 tests
|
||||
@@ -0,0 +1,146 @@
|
||||
--echo # MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: grants
|
||||
--echo #
|
||||
--echo # Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks grants and identifier handling.
|
||||
--echo # Recapture it after changing the MySQL-source code (recipe in
|
||||
--echo # std_data/mysql8_users_setup.sql).
|
||||
|
||||
--source mariadb-dump-mysql8-import.inc
|
||||
|
||||
--echo # 1. Every m8_* user landed.
|
||||
SELECT user, host FROM mysql.global_priv WHERE user LIKE 'm8\\_%' ORDER BY user, host;
|
||||
|
||||
--echo # 2. Simple static grants pass through unchanged.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_simple'@'%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_static_priv'@'%';
|
||||
|
||||
--echo # 3. Dynamic priv mapping (BINLOG_ADMIN -> BINLOG REPLAY/ADMIN).
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_dyn_mapped'@'%';
|
||||
|
||||
--echo # 4. Mixed dynamic priv (some mapped, some not).
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_dyn_mixed'@'%';
|
||||
|
||||
--echo # 5. WITH GRANT OPTION of a translated dynamic privilege is dropped.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_dyn_grant_opt'@'%';
|
||||
|
||||
--echo # 6. CREATE ROLE / DROP ROLE static privs dropped.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_role_static'@'%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_static_role_mix'@'%';
|
||||
|
||||
--echo # 7. Identifier parsing: column / db underscores don't false-positive.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_col_priv'@'%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_db_underscore'@'%';
|
||||
|
||||
--echo # 8. Identifier parsing: edge-case usernames and hostnames.
|
||||
SELECT user, host FROM mysql.global_priv
|
||||
WHERE user IN ('m8 edge space','m8_edge@in_name','m8_edge_wildcard_host')
|
||||
ORDER BY user, host;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_edge_wildcard_host'@'192.168.%.%';
|
||||
|
||||
--echo # 9. ALL PRIVILEGES expands and survives import.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_all_privs'@'%';
|
||||
|
||||
--echo # 10. Multi-scope grants for one user.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_multiscope'@'%';
|
||||
|
||||
--echo # 11. Routine privileges land in mysql.procs_priv.
|
||||
SELECT User, Host, Db, Routine_name, Routine_type, Proc_priv
|
||||
FROM mysql.procs_priv
|
||||
WHERE User LIKE 'm8\\_%'
|
||||
ORDER BY User, Host, Routine_type, Routine_name;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_routine_admin'@'%';
|
||||
|
||||
--echo # 12. PROXY grant lands in mysql.proxies_priv.
|
||||
SELECT User, Host, Proxied_user, Proxied_host
|
||||
FROM mysql.proxies_priv
|
||||
WHERE User='m8_proxy_user'
|
||||
ORDER BY User, Host;
|
||||
|
||||
--echo # 13. USAGE-only user lands with the implicit GRANT USAGE.
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_usage_only'@'%';
|
||||
|
||||
--echo # 14. System-schema grants (mysql.user, performance_schema.*).
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_system_grants'@'%';
|
||||
|
||||
--echo # 15. Same-name-different-host coverage.
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User IN ('m8_host_localhost','m8_host_dual')
|
||||
ORDER BY User, Host;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_host_localhost'@'localhost';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_host_dual'@'localhost';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_host_dual'@'%';
|
||||
|
||||
--echo # 16. Identifier edge cases: anonymous + embedded quote + UTF-8.
|
||||
--echo # ORDER BY BINARY for collation-stable byte-level ordering across
|
||||
--echo # servers running different default collations.
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User='' OR User='O\'Brien' OR User='m8_用户'
|
||||
ORDER BY BINARY User, Host;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'O\'Brien'@'%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_用户'@'%';
|
||||
|
||||
--echo # 17. Partial REVOKE. m8_partial_revoke has a wide GRANT on *.* and a
|
||||
--echo # partial REVOKE on m8_db.*. MariaDB has no equivalent, so only the
|
||||
--echo # wide GRANT lands (the dump carries a # WARNING for the REVOKE).
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_partial_revoke'@'%';
|
||||
|
||||
# The anonymous localhost user would shadow the m8_*@'%' logins.
|
||||
DROP USER ''@'localhost';
|
||||
|
||||
# The fixture users have caching_sha2_password, which needs TLS or RSA over TCP.
|
||||
# Switch the ones that connect; the semantics checked do not depend on the plugin.
|
||||
ALTER USER 'm8_col_priv'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_role_static'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_proc_exec'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
|
||||
--echo # m8_col_priv: SELECT/UPDATE on granted columns; non-granted denied.
|
||||
connect (c_col, localhost, m8_col_priv, pw, m8_db);
|
||||
SELECT col_a, col_underscore_b FROM m8_db.tab;
|
||||
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||
SELECT plain FROM m8_db.tab;
|
||||
UPDATE m8_db.tab SET col_a = 1 WHERE col_a IS NULL OR col_a IS NOT NULL;
|
||||
--error ER_COLUMNACCESS_DENIED_ERROR
|
||||
UPDATE m8_db.tab SET plain = 1;
|
||||
disconnect c_col;
|
||||
connection default;
|
||||
|
||||
--echo # m8_role_static landed with USAGE only -> CREATE/DROP ROLE denied.
|
||||
connect (c_rs, localhost, m8_role_static, pw);
|
||||
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
|
||||
CREATE ROLE m8_should_fail_role;
|
||||
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
|
||||
DROP ROLE IF EXISTS m8_role_a;
|
||||
disconnect c_rs;
|
||||
connection default;
|
||||
|
||||
--echo # m8_proc_exec: EXECUTE works; DROP PROCEDURE denied.
|
||||
connect (c_proc, localhost, m8_proc_exec, pw, m8_db);
|
||||
CALL m8_db.proc_one(42);
|
||||
--error ER_PROCACCESS_DENIED_ERROR
|
||||
DROP PROCEDURE m8_db.proc_one;
|
||||
disconnect c_proc;
|
||||
connection default;
|
||||
|
||||
--source mariadb-dump-mysql8-import-cleanup.inc
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
@@ -0,0 +1,3 @@
|
||||
--plugin-load-add=$AUTH_MYSQL_SHA2_SO
|
||||
--loose-caching-sha2-password
|
||||
--loose-disable-caching-sha2-password-auto-generate-rsa-keys
|
||||
@@ -0,0 +1,168 @@
|
||||
# MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: roles
|
||||
#
|
||||
# Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks the roles, default roles and hybrid accounts.
|
||||
# Recapture it after changing the MySQL-source code (recipe in
|
||||
# std_data/mysql8_users_setup.sql).
|
||||
SET NAMES utf8mb4;
|
||||
CREATE DATABASE IF NOT EXISTS m8_db;
|
||||
CREATE TABLE m8_db.tab (col_a INT, col_underscore_b INT, plain INT);
|
||||
CREATE TABLE m8_db.tab2 (id INT PRIMARY KEY, payload TEXT);
|
||||
USE m8_db;
|
||||
CREATE PROCEDURE proc_one(IN x INT) BEGIN SELECT x; END$$
|
||||
CREATE FUNCTION func_one(x INT) RETURNS INT DETERMINISTIC BEGIN RETURN x*2; END$$
|
||||
USE test;
|
||||
CREATE DATABASE IF NOT EXISTS `m8_under_score_db`;
|
||||
CALL mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
CALL mtr.add_suppression("Plugin 'caching_sha2_password' is of maturity level gamma");
|
||||
CREATE USER m8_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO m8_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO m8_restore WITH GRANT OPTION;
|
||||
NOT FOUND /(?:\A|\n)ERROR (?!1396 )\d/ in m8_import.err
|
||||
NOT FOUND /(?:\A|\n)ERROR 1396 (?![^\n]*'root'@'localhost')/ in m8_import.err
|
||||
FOUND 1 /sql_mode restored/ in m8_import.out
|
||||
# The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='m8_restore' OR Role='mariadb_dump_import_role';
|
||||
User Role
|
||||
DROP USER m8_restore;
|
||||
# 1. Roles imported with admin chain preserved.
|
||||
SELECT User, Host, Role, Admin_option FROM mysql.roles_mapping
|
||||
WHERE Role LIKE 'm8\\_%' OR User LIKE 'm8\\_%'
|
||||
ORDER BY Role, User, Host;
|
||||
User Host Role Admin_option
|
||||
m8_combined % m8_default_a N
|
||||
m8_default_role_many % m8_default_a N
|
||||
m8_default_role_one % m8_default_a N
|
||||
m8_shared_user_1 % m8_default_a N
|
||||
m8_shared_user_2 % m8_default_a N
|
||||
m8_default_role_many % m8_default_b N
|
||||
m8_hybrid_member % m8_hybrid N
|
||||
m8_admin_chain_root m8_role_a Y
|
||||
m8_simple % m8_role_a Y
|
||||
m8_role_a m8_role_b Y
|
||||
m8_hybrid m8_role_c N
|
||||
m8_hybrid % m8_role_c N
|
||||
m8_role_b m8_role_c Y
|
||||
m8_simple % m8_role_d N
|
||||
m8_dual_member % m8_role_dual N
|
||||
m8_shared_user_1 % m8_shared_role N
|
||||
m8_shared_user_2 % m8_shared_role Y
|
||||
m8_shared_user_3 % m8_shared_role N
|
||||
m8_shared_user_4 % m8_shared_role N
|
||||
m8_shared_user_5 % m8_shared_role Y
|
||||
# 1a. Role privileges land on the role
|
||||
# (including the one whose name contains a comment end).
|
||||
SHOW GRANTS FOR m8_role_a;
|
||||
Grants for m8_role_a
|
||||
GRANT `m8_role_b` TO `m8_role_a` WITH ADMIN OPTION
|
||||
GRANT USAGE ON *.* TO `m8_role_a`
|
||||
GRANT SELECT ON `m8_db`.* TO `m8_role_a`
|
||||
GRANT `m8_role_c` TO `m8_role_b` WITH ADMIN OPTION
|
||||
GRANT USAGE ON *.* TO `m8_role_b`
|
||||
GRANT INSERT ON `m8_db`.* TO `m8_role_b`
|
||||
GRANT USAGE ON *.* TO `m8_role_c`
|
||||
GRANT UPDATE ON `m8_db`.* TO `m8_role_c`
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User LIKE 'm8\\_role\\_%' OR User LIKE 'm8\\_role*/%' ORDER BY User, Host;
|
||||
User Host
|
||||
m8_role*/starslash
|
||||
m8_role_a
|
||||
m8_role_b
|
||||
m8_role_c
|
||||
m8_role_d
|
||||
m8_role_dual
|
||||
m8_role_static %
|
||||
# 2. Default-role assignment.
|
||||
SELECT User, Host, default_role
|
||||
FROM mysql.user
|
||||
WHERE User IN ('m8_default_role_one','m8_default_role_many','m8_combined')
|
||||
AND default_role <> ''
|
||||
ORDER BY User, Host;
|
||||
User Host default_role
|
||||
m8_combined % m8_default_a
|
||||
m8_default_role_many % m8_default_a
|
||||
m8_default_role_one % m8_default_a
|
||||
# 3. Hybrid account (a login user that is also granted as a role):
|
||||
# it lands as a user with its privilege and as a role with the same
|
||||
# grants, its role membership, and its members.
|
||||
SELECT User, Host, is_role FROM mysql.user
|
||||
WHERE User LIKE 'm8\\_hybrid%' ORDER BY User, Host;
|
||||
User Host is_role
|
||||
m8_hybrid Y
|
||||
m8_hybrid % N
|
||||
m8_hybrid_member % N
|
||||
SHOW GRANTS FOR 'm8_hybrid'@'%';
|
||||
Grants for m8_hybrid@%
|
||||
GRANT `m8_role_c` TO `m8_hybrid`@`%`
|
||||
GRANT USAGE ON *.* TO `m8_hybrid`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
GRANT SELECT ON `m8_db`.`tab` TO `m8_hybrid`@`%`
|
||||
SHOW GRANTS FOR m8_hybrid;
|
||||
Grants for m8_hybrid
|
||||
GRANT `m8_role_c` TO `m8_hybrid`
|
||||
GRANT USAGE ON *.* TO `m8_hybrid`
|
||||
GRANT SELECT ON `m8_db`.`tab` TO `m8_hybrid`
|
||||
GRANT USAGE ON *.* TO `m8_role_c`
|
||||
GRANT UPDATE ON `m8_db`.* TO `m8_role_c`
|
||||
SHOW GRANTS FOR 'm8_hybrid_member'@'%';
|
||||
Grants for m8_hybrid_member@%
|
||||
GRANT `m8_hybrid` TO `m8_hybrid_member`@`%`
|
||||
GRANT USAGE ON *.* TO `m8_hybrid_member`@`%` IDENTIFIED VIA caching_sha2_password USING '<hash>'
|
||||
# 4. One role name on two MySQL hosts lands as one hostless role holding
|
||||
# both hosts' privileges, granted once to the shared member.
|
||||
SELECT User, Host, is_role FROM mysql.user WHERE User='m8_role_dual';
|
||||
User Host is_role
|
||||
m8_role_dual Y
|
||||
SHOW GRANTS FOR m8_role_dual;
|
||||
Grants for m8_role_dual
|
||||
GRANT USAGE ON *.* TO `m8_role_dual`
|
||||
GRANT SELECT, INSERT ON `m8_db`.`tab` TO `m8_role_dual`
|
||||
SELECT User, Host, Role FROM mysql.roles_mapping WHERE Role='m8_role_dual';
|
||||
User Host Role
|
||||
m8_dual_member % m8_role_dual
|
||||
DROP USER ''@'localhost';
|
||||
ALTER USER 'm8_simple'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_hybrid'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_hybrid_member'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
# m8_simple: schema SELECT works; INSERT denied until m8_role_a, which
|
||||
# reaches INSERT through m8_role_b, is enabled.
|
||||
connect c_simple, localhost, m8_simple, pw, m8_db;
|
||||
SELECT COUNT(*) AS rows_visible FROM m8_db.tab;
|
||||
rows_visible
|
||||
0
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
ERROR 42000: INSERT command denied to user 'm8_simple'@'localhost' for table `m8_db`.`tab`
|
||||
SET ROLE m8_role_a;
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
SELECT COUNT(*) AS rows_via_role FROM m8_db.tab;
|
||||
rows_via_role
|
||||
1
|
||||
disconnect c_simple;
|
||||
connection default;
|
||||
DELETE FROM m8_db.tab;
|
||||
# m8_admin_chain_root: WITH ADMIN OPTION on m8_role_a is preserved.
|
||||
SELECT User, Host, Admin_option
|
||||
FROM mysql.roles_mapping
|
||||
WHERE User='m8_admin_chain_root' AND Role='m8_role_a';
|
||||
User Host Admin_option
|
||||
m8_admin_chain_root Y
|
||||
# m8_hybrid: logs in as a user; m8_hybrid_member gets its privileges
|
||||
# only through the role.
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
connect c_hyb, localhost, m8_hybrid, pw, m8_db;
|
||||
SELECT COUNT(*) AS rows_as_user FROM m8_db.tab;
|
||||
rows_as_user
|
||||
1
|
||||
disconnect c_hyb;
|
||||
connect c_hm, localhost, m8_hybrid_member, pw;
|
||||
SELECT COUNT(*) FROM m8_db.tab;
|
||||
ERROR 42000: SELECT command denied to user 'm8_hybrid_member'@'localhost' for table `m8_db`.`tab`
|
||||
SET ROLE m8_hybrid;
|
||||
SELECT COUNT(*) AS rows_via_role FROM m8_db.tab;
|
||||
rows_via_role
|
||||
1
|
||||
disconnect c_hm;
|
||||
connection default;
|
||||
DELETE FROM m8_db.tab;
|
||||
DROP DATABASE IF EXISTS m8_db;
|
||||
DROP DATABASE IF EXISTS `m8_under_score_db`;
|
||||
# End of 13.1 tests
|
||||
@@ -0,0 +1,91 @@
|
||||
--echo # MDEV-24557 mariadb-dump: import MySQL 8.x users, grants, roles: roles
|
||||
--echo #
|
||||
--echo # Imports mysql8_users_dump.sql, a dump of MySQL 26.7.0, and checks the roles, default roles and hybrid accounts.
|
||||
--echo # Recapture it after changing the MySQL-source code (recipe in
|
||||
--echo # std_data/mysql8_users_setup.sql).
|
||||
|
||||
--source mariadb-dump-mysql8-import.inc
|
||||
|
||||
--echo # 1. Roles imported with admin chain preserved.
|
||||
SELECT User, Host, Role, Admin_option FROM mysql.roles_mapping
|
||||
WHERE Role LIKE 'm8\\_%' OR User LIKE 'm8\\_%'
|
||||
ORDER BY Role, User, Host;
|
||||
|
||||
--echo # 1a. Role privileges land on the role
|
||||
--echo # (including the one whose name contains a comment end).
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR m8_role_a;
|
||||
SELECT User, Host FROM mysql.global_priv
|
||||
WHERE User LIKE 'm8\\_role\\_%' OR User LIKE 'm8\\_role*/%' ORDER BY User, Host;
|
||||
|
||||
--echo # 2. Default-role assignment.
|
||||
SELECT User, Host, default_role
|
||||
FROM mysql.user
|
||||
WHERE User IN ('m8_default_role_one','m8_default_role_many','m8_combined')
|
||||
AND default_role <> ''
|
||||
ORDER BY User, Host;
|
||||
|
||||
--echo # 3. Hybrid account (a login user that is also granted as a role):
|
||||
--echo # it lands as a user with its privilege and as a role with the same
|
||||
--echo # grants, its role membership, and its members.
|
||||
SELECT User, Host, is_role FROM mysql.user
|
||||
WHERE User LIKE 'm8\\_hybrid%' ORDER BY User, Host;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_hybrid'@'%';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR m8_hybrid;
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR 'm8_hybrid_member'@'%';
|
||||
|
||||
--echo # 4. One role name on two MySQL hosts lands as one hostless role holding
|
||||
--echo # both hosts' privileges, granted once to the shared member.
|
||||
SELECT User, Host, is_role FROM mysql.user WHERE User='m8_role_dual';
|
||||
--replace_regex /USING '(?:[^']|'')*'/USING '<hash>'/
|
||||
SHOW GRANTS FOR m8_role_dual;
|
||||
SELECT User, Host, Role FROM mysql.roles_mapping WHERE Role='m8_role_dual';
|
||||
|
||||
# The anonymous localhost user would shadow the m8_*@'%' logins.
|
||||
DROP USER ''@'localhost';
|
||||
|
||||
# The fixture users have caching_sha2_password, which needs TLS or RSA over TCP.
|
||||
# Switch the ones that connect; the semantics checked do not depend on the plugin.
|
||||
ALTER USER 'm8_simple'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_hybrid'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
ALTER USER 'm8_hybrid_member'@'%' IDENTIFIED VIA mysql_native_password USING PASSWORD('pw');
|
||||
|
||||
--echo # m8_simple: schema SELECT works; INSERT denied until m8_role_a, which
|
||||
--echo # reaches INSERT through m8_role_b, is enabled.
|
||||
connect (c_simple, localhost, m8_simple, pw, m8_db);
|
||||
SELECT COUNT(*) AS rows_visible FROM m8_db.tab;
|
||||
--error ER_TABLEACCESS_DENIED_ERROR
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
SET ROLE m8_role_a;
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
SELECT COUNT(*) AS rows_via_role FROM m8_db.tab;
|
||||
disconnect c_simple;
|
||||
connection default;
|
||||
DELETE FROM m8_db.tab;
|
||||
|
||||
--echo # m8_admin_chain_root: WITH ADMIN OPTION on m8_role_a is preserved.
|
||||
SELECT User, Host, Admin_option
|
||||
FROM mysql.roles_mapping
|
||||
WHERE User='m8_admin_chain_root' AND Role='m8_role_a';
|
||||
|
||||
--echo # m8_hybrid: logs in as a user; m8_hybrid_member gets its privileges
|
||||
--echo # only through the role.
|
||||
INSERT INTO m8_db.tab VALUES (1,1,1);
|
||||
connect (c_hyb, localhost, m8_hybrid, pw, m8_db);
|
||||
SELECT COUNT(*) AS rows_as_user FROM m8_db.tab;
|
||||
disconnect c_hyb;
|
||||
connect (c_hm, localhost, m8_hybrid_member, pw);
|
||||
--error ER_TABLEACCESS_DENIED_ERROR
|
||||
SELECT COUNT(*) FROM m8_db.tab;
|
||||
SET ROLE m8_hybrid;
|
||||
SELECT COUNT(*) AS rows_via_role FROM m8_db.tab;
|
||||
disconnect c_hm;
|
||||
connection default;
|
||||
DELETE FROM m8_db.tab;
|
||||
|
||||
--source mariadb-dump-mysql8-import-cleanup.inc
|
||||
|
||||
--echo # End of 13.1 tests
|
||||
@@ -0,0 +1,57 @@
|
||||
--source include/not_embedded.inc
|
||||
|
||||
if (`select count(*) = 0 from information_schema.plugins where plugin_name = 'caching_sha2_password'`)
|
||||
{
|
||||
--skip Needs caching_sha2_password plugin
|
||||
}
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
--source include/backup_priv_tables.inc
|
||||
|
||||
# Schema referenced by table-/column-/routine-level grants in the dump.
|
||||
CREATE DATABASE IF NOT EXISTS m8_db;
|
||||
CREATE TABLE m8_db.tab (col_a INT, col_underscore_b INT, plain INT);
|
||||
CREATE TABLE m8_db.tab2 (id INT PRIMARY KEY, payload TEXT);
|
||||
USE m8_db;
|
||||
DELIMITER $$;
|
||||
CREATE PROCEDURE proc_one(IN x INT) BEGIN SELECT x; END$$
|
||||
CREATE FUNCTION func_one(x INT) RETURNS INT DETERMINISTIC BEGIN RETURN x*2; END$$
|
||||
DELIMITER ;$$
|
||||
USE test;
|
||||
CREATE DATABASE IF NOT EXISTS `m8_under_score_db`;
|
||||
|
||||
# auth_mysql_sha2 is loaded via .opt - silence its load-time warnings.
|
||||
CALL mtr.add_suppression('failed to read private_key.pem: 2 "No such file or directory"');
|
||||
CALL mtr.add_suppression("Plugin 'caching_sha2_password' is of maturity level gamma");
|
||||
|
||||
CREATE USER m8_restore IDENTIFIED BY 'r';
|
||||
GRANT ALL ON *.* TO m8_restore WITH GRANT OPTION;
|
||||
GRANT PROXY ON ''@'%' TO m8_restore WITH GRANT OPTION;
|
||||
|
||||
# --force goes on past the expected ER 1396 for root@localhost, which
|
||||
# already exists on MariaDB; any other error shows up as FOUND.
|
||||
# The appended query shows whether the dump restores the session sql_mode.
|
||||
--copy_file $MYSQLTEST_VARDIR/std_data/mysql8_users_dump.sql $MYSQLTEST_VARDIR/tmp/m8_import.sql
|
||||
--append_file $MYSQLTEST_VARDIR/tmp/m8_import.sql
|
||||
SELECT IF(@@sql_mode = @@global.sql_mode, 'sql_mode restored', 'sql_mode LEAKED');
|
||||
EOF
|
||||
--exec $MYSQL --user=m8_restore --password=r --force -N < $MYSQLTEST_VARDIR/tmp/m8_import.sql > $MYSQLTEST_VARDIR/tmp/m8_import.out 2> $MYSQLTEST_VARDIR/tmp/m8_import.err
|
||||
|
||||
--let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/m8_import.err
|
||||
--let SEARCH_PATTERN=(?:\A|\n)ERROR (?!1396 )\d
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_PATTERN=(?:\A|\n)ERROR 1396 (?![^\n]*'root'@'localhost')
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_FILE=$MYSQLTEST_VARDIR/tmp/m8_import.out
|
||||
--let SEARCH_PATTERN=sql_mode restored
|
||||
--let SEARCH_ABORT=NOT FOUND
|
||||
--source include/search_pattern_in_file.inc
|
||||
--let SEARCH_ABORT=
|
||||
|
||||
--echo # The import role is gone and the restoring user is not left as admin:
|
||||
SELECT User, Role FROM mysql.roles_mapping
|
||||
WHERE User='m8_restore' OR Role='mariadb_dump_import_role';
|
||||
--disable_warnings
|
||||
DROP USER m8_restore;
|
||||
--enable_warnings
|
||||
@@ -1,3 +1,7 @@
|
||||
ADD_EXECUTABLE(import_util-t import_util-t.cc)
|
||||
TARGET_LINK_LIBRARIES(import_util-t PRIVATE import_util mytap)
|
||||
ADD_TEST(import_util import_util-t)
|
||||
|
||||
ADD_EXECUTABLE(mysql8_translate-t mysql8_translate-t.cc)
|
||||
TARGET_LINK_LIBRARIES(mysql8_translate-t PRIVATE mysql8_translate mytap mysys strings dbug)
|
||||
ADD_TEST(mysql8_translate mysql8_translate-t)
|
||||
|
||||
425
unittest/client/mysql8_translate-t.cc
Normal file
425
unittest/client/mysql8_translate-t.cc
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
Copyright (c) 2026, MariaDB Corporation.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
|
||||
*/
|
||||
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include "mysql8_translate.h"
|
||||
|
||||
#include <tap.h>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
Inputs marked "captured" are SHOW GRANTS / SHOW CREATE USER output of
|
||||
a MySQL 26.7.0 or 5.7 server, the rest are hand-written edge cases.
|
||||
*/
|
||||
|
||||
/* Run fn(out) and return what it wrote to out. */
|
||||
template <class F> static std::string capture(F fn)
|
||||
{
|
||||
FILE *f= tmpfile();
|
||||
if (!f)
|
||||
{
|
||||
diag("tmpfile() failed");
|
||||
return "<tmpfile() failed>";
|
||||
}
|
||||
fn(f);
|
||||
std::string res;
|
||||
char buf[512];
|
||||
size_t n;
|
||||
rewind(f);
|
||||
while ((n= fread(buf, 1, sizeof(buf), f)) > 0)
|
||||
res.append(buf, n);
|
||||
fclose(f);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void check_grant(const char *name, const char *grant, bool handled,
|
||||
const char *expected, const char *role_name= NULL)
|
||||
{
|
||||
bool res= false;
|
||||
std::string out= capture([&](FILE *f)
|
||||
{ res= translate_mysql8_grant(grant, f, role_name); });
|
||||
ok(res == handled && out == expected, "%s", name);
|
||||
if (res != handled || out != expected)
|
||||
diag("handled=%d, output:\n%s", (int) res, out.c_str());
|
||||
}
|
||||
|
||||
static void check_user(const char *name, const char *user_str,
|
||||
bool replace_into, bool ignore, const char *expected)
|
||||
{
|
||||
std::string out= capture([&](FILE *f)
|
||||
{ translate_mysql8_create_user(user_str, f, replace_into, ignore); });
|
||||
ok(out == expected, "%s", name);
|
||||
if (out != expected)
|
||||
diag("output:\n%s", out.c_str());
|
||||
}
|
||||
|
||||
#define DROPPED(priv) \
|
||||
"# WARNING: dropped MySQL privilege " priv " (no MariaDB equivalent)\n"
|
||||
#define GRANT_OPT_DROPPED \
|
||||
"# WARNING: WITH GRANT OPTION dropped from a translated privilege: " \
|
||||
"MariaDB would make all global privileges of the account grantable\n"
|
||||
#define POLICY_DROPPED(clause) \
|
||||
"# WARNING: password policy clause dropped (no MariaDB equivalent): " \
|
||||
clause "\n"
|
||||
#define OPTION_DROPPED(clause) \
|
||||
"# WARNING: account option dropped (no MariaDB equivalent): " clause "\n"
|
||||
#define AS_DROPPED(clause) \
|
||||
"# WARNING: partial-revokes AS clause dropped (no MariaDB equivalent): " \
|
||||
clause "; the grant below is wider than on the MySQL source\n"
|
||||
|
||||
static void test_grant()
|
||||
{
|
||||
check_grant("captured: dynamic privileges mapped or dropped",
|
||||
"GRANT AUDIT_ABORT_EXEMPT,AUTHENTICATION_POLICY_ADMIN,BACKUP_ADMIN,"
|
||||
"CLONE_ADMIN,CONNECTION_ADMIN,FIREWALL_EXEMPT,PERSIST_RO_VARIABLES_ADMIN,"
|
||||
"SESSION_VARIABLES_ADMIN,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN "
|
||||
"ON *.* TO `mysql.session`@`localhost`", true,
|
||||
DROPPED("AUDIT_ABORT_EXEMPT")
|
||||
DROPPED("AUTHENTICATION_POLICY_ADMIN")
|
||||
DROPPED("BACKUP_ADMIN")
|
||||
DROPPED("CLONE_ADMIN")
|
||||
DROPPED("FIREWALL_EXEMPT")
|
||||
DROPPED("PERSIST_RO_VARIABLES_ADMIN")
|
||||
DROPPED("SESSION_VARIABLES_ADMIN")
|
||||
DROPPED("SYSTEM_USER")
|
||||
DROPPED("SYSTEM_VARIABLES_ADMIN")
|
||||
"/*M!100005 GRANT CONNECTION ADMIN ON *.* TO `mysql.session`@`localhost` */;\n");
|
||||
|
||||
check_grant("captured: static privileges pass through",
|
||||
"GRANT SHUTDOWN, SUPER ON *.* TO `mysql.session`@`localhost`", false, "");
|
||||
|
||||
check_grant("unlisted dynamic privilege is dropped",
|
||||
"GRANT MANAGE_DATA_MASKING_POLICY,SELECT ON *.* TO `u`@`h`", true,
|
||||
DROPPED("MANAGE_DATA_MASKING_POLICY")
|
||||
"/*M!100005 GRANT SELECT ON *.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("two privileges mapped to one are deduplicated",
|
||||
"GRANT BINLOG_ADMIN,REPLICATION_APPLIER ON *.* TO `u`@`h`", true,
|
||||
"/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN ON *.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("privileges whose MariaDB counterpart is wider are dropped",
|
||||
"GRANT ALLOW_NONEXISTENT_DEFINER,SERVICE_CONNECTION_ADMIN,SET_ANY_DEFINER "
|
||||
"ON *.* TO `u`@`h`", true,
|
||||
DROPPED("ALLOW_NONEXISTENT_DEFINER")
|
||||
DROPPED("SERVICE_CONNECTION_ADMIN")
|
||||
"/*M!100005 GRANT SET USER ON *.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("grant option of a translated privilege is dropped",
|
||||
"GRANT BINLOG_ADMIN ON *.* TO `u`@`h` WITH GRANT OPTION", true,
|
||||
GRANT_OPT_DROPPED
|
||||
"/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN ON *.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("grant option of a translated privilege on a role grantee",
|
||||
"GRANT BINLOG_ADMIN ON *.* TO `r1`@`%` WITH GRANT OPTION", true,
|
||||
GRANT_OPT_DROPPED
|
||||
"/*M!100005 GRANT BINLOG REPLAY, BINLOG ADMIN ON *.* TO `r1` */;\n",
|
||||
"`r1`");
|
||||
|
||||
check_grant("REPLICATION_SLAVE_ADMIN, SHOW_ROUTINE and SET_ANY_DEFINER map",
|
||||
"GRANT REPLICATION_SLAVE_ADMIN,SHOW_ROUTINE,SET_ANY_DEFINER,"
|
||||
"CONNECTION_ADMIN,REPLICATION_APPLIER ON *.* TO `u`@`h`", true,
|
||||
"/*M!100005 GRANT REPLICATION SLAVE ADMIN, SHOW CREATE ROUTINE, SET USER, "
|
||||
"CONNECTION ADMIN, BINLOG REPLAY ON *.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("grant option of a static privilege survives a dropped one",
|
||||
"GRANT SELECT, CREATE ROLE ON *.* TO `u`@`h` WITH GRANT OPTION", true,
|
||||
DROPPED("CREATE ROLE")
|
||||
"/*M!100005 GRANT SELECT ON *.* TO `u`@`h` WITH GRANT OPTION */;\n");
|
||||
|
||||
check_grant("column list is one privilege",
|
||||
"GRANT SELECT (a, b), BACKUP_ADMIN ON `d`.`t` TO `u`@`h`", true,
|
||||
DROPPED("BACKUP_ADMIN")
|
||||
"/*M!100005 GRANT SELECT (a, b) ON `d`.`t` TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("role grant is skipped",
|
||||
"GRANT `r1`@`%` TO `u`@`%`", true, "");
|
||||
|
||||
check_grant("AS clause is dropped",
|
||||
"GRANT SELECT ON *.* TO `u`@`h` WITH GRANT OPTION AS `root`@`localhost`",
|
||||
true,
|
||||
AS_DROPPED("AS `root`@`localhost`")
|
||||
"/*M!100005 GRANT SELECT ON *.* TO `u`@`h` WITH GRANT OPTION */;\n");
|
||||
|
||||
check_grant("AS clause is not warned about when no privilege survives",
|
||||
"GRANT BACKUP_ADMIN ON *.* TO `u`@`h` AS `root`@`localhost`", true,
|
||||
DROPPED("BACKUP_ADMIN"));
|
||||
|
||||
check_grant("backtick name ending in a backslash",
|
||||
"GRANT SELECT ON `a\\`.* TO `u`@`h` AS `root`@`localhost`", true,
|
||||
AS_DROPPED("AS `root`@`localhost`")
|
||||
"/*M!100005 GRANT SELECT ON `a\\`.* TO `u`@`h` */;\n");
|
||||
|
||||
check_grant("role grantee loses its host",
|
||||
"GRANT SELECT ON `d`.* TO `r1`@`%`", true,
|
||||
"/*M!100005 GRANT SELECT ON `d`.* TO `r1` */;\n", "`r1`");
|
||||
|
||||
check_grant("role grantee with an AS clause",
|
||||
"GRANT SELECT ON `d`.* TO `r1`@`%` AS `root`@`localhost`", true,
|
||||
AS_DROPPED("AS `root`@`localhost`")
|
||||
"/*M!100005 GRANT SELECT ON `d`.* TO `r1` */;\n", "`r1`");
|
||||
|
||||
check_grant("GRANT longer than the stack buffer",
|
||||
("GRANT SELECT ON `" + std::string(1100, 'd') + "`.* TO `r1`@`%`").c_str(),
|
||||
true,
|
||||
("/*M!100005 GRANT SELECT ON `" + std::string(1100, 'd') +
|
||||
"`.* TO `r1` */;\n").c_str(), "`r1`");
|
||||
|
||||
check_grant("role grantee with GRANT OPTION and USAGE",
|
||||
"GRANT USAGE ON *.* TO `r1`@`%` WITH GRANT OPTION", true,
|
||||
"/*M!100005 GRANT USAGE ON *.* TO `r1` WITH GRANT OPTION */;\n", "`r1`");
|
||||
|
||||
check_grant("role name containing a comment terminator is split",
|
||||
"GRANT USAGE ON *.* TO `r*/x`@`%`", true,
|
||||
"/*M!100203 EXECUTE IMMEDIATE CONCAT('GRANT USAGE ON *.* TO `r*', "
|
||||
"'/x`') */;\n", "`r*/x`");
|
||||
|
||||
check_grant("comment terminator in an identifier is split",
|
||||
"GRANT SELECT, BACKUP_ADMIN ON `d*/x`.* TO `u`@`h`", true,
|
||||
DROPPED("BACKUP_ADMIN")
|
||||
"/*M!100203 EXECUTE IMMEDIATE CONCAT('GRANT SELECT ON `d*', "
|
||||
"'/x`.* TO `u`@`h`') */;\n");
|
||||
}
|
||||
|
||||
static void test_revoke()
|
||||
{
|
||||
std::string out= capture([](FILE *f)
|
||||
{ translate_mysql8_revoke("REVOKE INSERT ON `d\nx`.* FROM `u`@`h`", f); });
|
||||
ok(out ==
|
||||
"# WARNING: partial REVOKE has no MariaDB equivalent, ignored: "
|
||||
"REVOKE INSERT ON `d x`.* FROM `u`@`h`\n"
|
||||
"# the wider GRANT remains in effect; re-grant at the narrower "
|
||||
"scope if needed.\n", "partial REVOKE is a warning, newline folded");
|
||||
}
|
||||
|
||||
static void test_create_user()
|
||||
{
|
||||
check_user("captured: clauses without MariaDB equivalent are dropped",
|
||||
" `root`@`localhost` IDENTIFIED WITH 'caching_sha2_password' REQUIRE NONE "
|
||||
"PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK PASSWORD HISTORY DEFAULT "
|
||||
"PASSWORD REUSE INTERVAL DEFAULT PASSWORD REQUIRE CURRENT DEFAULT",
|
||||
false, false,
|
||||
"CREATE USER `root`@`localhost` IDENTIFIED WITH 'caching_sha2_password' "
|
||||
"REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;\n");
|
||||
|
||||
check_user("captured: locked account keeps ACCOUNT LOCK",
|
||||
" `mysql.session`@`localhost` IDENTIFIED WITH 'caching_sha2_password' "
|
||||
"AS '$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED' "
|
||||
"REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK PASSWORD HISTORY DEFAULT "
|
||||
"PASSWORD REUSE INTERVAL DEFAULT PASSWORD REQUIRE CURRENT DEFAULT",
|
||||
false, false,
|
||||
"CREATE USER `mysql.session`@`localhost` IDENTIFIED WITH "
|
||||
"'caching_sha2_password' AS "
|
||||
"'$A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED' "
|
||||
"REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;\n");
|
||||
|
||||
check_user("unsupported plugin: IDENTIFIED dropped, account locked",
|
||||
" `u`@`h` IDENTIFIED WITH 'sha256_password' AS 'x' REQUIRE NONE "
|
||||
"PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK", false, false,
|
||||
"CREATE USER `u`@`h` REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin 'sha256_password' not shipped with MariaDB; "
|
||||
"user created with no password and ACCOUNT LOCK - set a password and "
|
||||
"ACCOUNT UNLOCK before login\n");
|
||||
|
||||
check_user("unsupported plugin without an ACCOUNT clause",
|
||||
" `u`@`h` IDENTIFIED WITH 'sha256_password' AS 'x'", false, false,
|
||||
"CREATE USER `u`@`h` ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin 'sha256_password' not shipped with MariaDB; "
|
||||
"user created with no password and ACCOUNT LOCK - set a password and "
|
||||
"ACCOUNT UNLOCK before login\n");
|
||||
|
||||
check_user("second authentication factor is dropped",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' AND IDENTIFIED "
|
||||
"WITH 'authentication_ldap_simple' AS 'x' REQUIRE NONE", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"REQUIRE NONE;\n"
|
||||
"# WARNING: additional authentication factor 'authentication_ldap_simple' "
|
||||
"dropped: MariaDB cannot require it\n");
|
||||
|
||||
check_user("second factor with a supported plugin is warned about",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' AND IDENTIFIED "
|
||||
"WITH 'mysql_native_password' AS 'h2' REQUIRE NONE", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"REQUIRE NONE;\n"
|
||||
"# WARNING: additional authentication factor 'mysql_native_password' "
|
||||
"dropped: MariaDB cannot require it\n");
|
||||
|
||||
check_user("newline in a plugin name is folded",
|
||||
" `u`@`h` IDENTIFIED WITH 'bad\nDROP USER x;\nplug' AS 'x'", false, false,
|
||||
"CREATE USER `u`@`h` ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin 'bad DROP USER x; plug' not shipped with "
|
||||
"MariaDB; user created with no password and ACCOUNT LOCK - set a "
|
||||
"password and ACCOUNT UNLOCK before login\n");
|
||||
|
||||
{
|
||||
std::string name(700, 'p');
|
||||
check_user("long plugin name keeps the warning on its own line",
|
||||
(" `u`@`h` IDENTIFIED WITH '" + name + "' AS 'x'").c_str(), false, false,
|
||||
("CREATE USER `u`@`h` ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin '" + name + "' not shipped with MariaDB; user "
|
||||
"created with no password and ACCOUNT LOCK - set a password and "
|
||||
"ACCOUNT UNLOCK before login\n").c_str());
|
||||
}
|
||||
|
||||
check_user("--replace",
|
||||
" `u`@`h` IDENTIFIED WITH 'mysql_native_password' AS '*ABC' REQUIRE NONE",
|
||||
true, false,
|
||||
"CREATE /*M!100103 OR REPLACE */ USER `u`@`h` IDENTIFIED "
|
||||
"WITH 'mysql_native_password' AS '*ABC' REQUIRE NONE;\n");
|
||||
|
||||
check_user("--ignore",
|
||||
" `u`@`h` IDENTIFIED WITH 'mysql_native_password' AS '*ABC' REQUIRE NONE",
|
||||
false, true,
|
||||
"CREATE USER IF NOT EXISTS `u`@`h` IDENTIFIED "
|
||||
"WITH 'mysql_native_password' AS '*ABC' REQUIRE NONE;\n");
|
||||
|
||||
check_user("dropped clauses around kept ones",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' COMMENT 'c' "
|
||||
"FAILED_LOGIN_ATTEMPTS 3 REQUIRE SSL PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK "
|
||||
"PASSWORD REQUIRE CURRENT DEFAULT PASSWORD_LOCK_TIME 2 ATTRIBUTE '{}'",
|
||||
false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"REQUIRE SSL PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;\n"
|
||||
OPTION_DROPPED("COMMENT 'c'")
|
||||
OPTION_DROPPED("FAILED_LOGIN_ATTEMPTS 3")
|
||||
OPTION_DROPPED("PASSWORD_LOCK_TIME 2")
|
||||
OPTION_DROPPED("ATTRIBUTE '{}'"));
|
||||
|
||||
check_user("captured from 5.7: sha256_password is dropped and locked",
|
||||
" `u`@`h` IDENTIFIED WITH 'sha256_password' AS '$5$x' REQUIRE NONE "
|
||||
"PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK", false, false,
|
||||
"CREATE USER `u`@`h` REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin 'sha256_password' not shipped with MariaDB; "
|
||||
"user created with no password and ACCOUNT LOCK - set a password and "
|
||||
"ACCOUNT UNLOCK before login\n");
|
||||
|
||||
check_user("captured from 5.7: native password and policy are kept",
|
||||
" `u`@`h` IDENTIFIED WITH 'mysql_native_password' AS '*ABC' REQUIRE "
|
||||
"SUBJECT '/C=SE' ISSUER '/C=SE' CIPHER 'AES256-SHA' WITH "
|
||||
"MAX_QUERIES_PER_HOUR 10 PASSWORD EXPIRE INTERVAL 30 DAY ACCOUNT UNLOCK",
|
||||
false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'mysql_native_password' AS '*ABC' "
|
||||
"REQUIRE SUBJECT '/C=SE' ISSUER '/C=SE' CIPHER 'AES256-SHA' WITH "
|
||||
"MAX_QUERIES_PER_HOUR 10 PASSWORD EXPIRE INTERVAL 30 DAY ACCOUNT UNLOCK;\n");
|
||||
|
||||
check_user("keyword inside a quoted name is not a clause",
|
||||
" `x REQUIRE y`@`h` REQUIRE SSL", false, false,
|
||||
"CREATE USER `x REQUIRE y`@`h` REQUIRE SSL;\n");
|
||||
|
||||
check_user("escaped quote inside the hash",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS '$A$005$x\\'y REQUIRE "
|
||||
"z' PASSWORD HISTORY DEFAULT", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS "
|
||||
"'$A$005$x\\'y REQUIRE z';\n");
|
||||
|
||||
check_user("non-default password policy clauses are warned about",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 30 DAY "
|
||||
"PASSWORD REQUIRE CURRENT OPTIONAL", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1';\n"
|
||||
POLICY_DROPPED("PASSWORD HISTORY 5")
|
||||
POLICY_DROPPED("PASSWORD REUSE INTERVAL 30 DAY")
|
||||
POLICY_DROPPED("PASSWORD REQUIRE CURRENT OPTIONAL"));
|
||||
|
||||
check_user("account options are warned about",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME UNBOUNDED COMMENT 'a\nb' "
|
||||
"ATTRIBUTE '{\"x\": 1}'", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1';\n"
|
||||
OPTION_DROPPED("FAILED_LOGIN_ATTEMPTS 3")
|
||||
OPTION_DROPPED("PASSWORD_LOCK_TIME UNBOUNDED")
|
||||
OPTION_DROPPED("COMMENT 'a b'")
|
||||
OPTION_DROPPED("ATTRIBUTE '{\"x\": 1}'"));
|
||||
|
||||
check_user("zero values are the absence of a policy",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"PASSWORD HISTORY 0 PASSWORD REUSE INTERVAL 0 DAY "
|
||||
"FAILED_LOGIN_ATTEMPTS 0 PASSWORD_LOCK_TIME 0", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1';\n");
|
||||
|
||||
check_user("a value starting with 0 is a policy",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"PASSWORD HISTORY 05", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1';\n"
|
||||
POLICY_DROPPED("PASSWORD HISTORY 05"));
|
||||
|
||||
check_user("bare PASSWORD REQUIRE CURRENT is warned about",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"PASSWORD REQUIRE CURRENT", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1';\n"
|
||||
POLICY_DROPPED("PASSWORD REQUIRE CURRENT"));
|
||||
|
||||
check_user("unsupported first plugin and a second factor",
|
||||
" `u`@`h` IDENTIFIED WITH 'sha256_password' AS 'x' AND IDENTIFIED WITH "
|
||||
"'caching_sha2_password' AS 'y' REQUIRE NONE", false, false,
|
||||
"CREATE USER `u`@`h` REQUIRE NONE ACCOUNT LOCK;\n"
|
||||
"# WARNING: auth plugin 'sha256_password' not shipped with MariaDB; "
|
||||
"user created with no password and ACCOUNT LOCK - set a password and "
|
||||
"ACCOUNT UNLOCK before login\n"
|
||||
"# WARNING: additional authentication factor 'caching_sha2_password' "
|
||||
"dropped: MariaDB cannot require it\n");
|
||||
|
||||
check_user("DEFAULT ROLE is dropped",
|
||||
" `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' DEFAULT ROLE "
|
||||
"`r`@`%` REQUIRE NONE", false, false,
|
||||
"CREATE USER `u`@`h` IDENTIFIED WITH 'caching_sha2_password' AS 'h1' "
|
||||
"REQUIRE NONE;\n");
|
||||
}
|
||||
|
||||
static void test_emit()
|
||||
{
|
||||
std::string out= capture([](FILE *f)
|
||||
{ dump_emit_comment_warning(f, "a %s b", "x\ny\rz"); });
|
||||
ok(out == "# a x y z b\n", "CR/LF are folded in a comment warning");
|
||||
|
||||
out= capture([](FILE *f)
|
||||
{ dump_emit_versioned_wrap(f, "/*M!100005 ", "DROP ROLE %s", "`a*/b`"); });
|
||||
ok(out == "/*M!100203 EXECUTE IMMEDIATE CONCAT('DROP ROLE `a*', '/b`') "
|
||||
"*/;\n", "comment terminator is split");
|
||||
|
||||
out= capture([](FILE *f)
|
||||
{ dump_emit_versioned_wrap(f, "/*M!100005 ", "DROP ROLE %s",
|
||||
"'a\\*/*/'"); });
|
||||
ok(out == "/*M!100203 EXECUTE IMMEDIATE CONCAT('DROP ROLE ''a\\\\*', '/*', "
|
||||
"'/''') */;\n", "quote and backslash escaped, terminators split");
|
||||
|
||||
out= capture([](FILE *f)
|
||||
{ dump_put_comment_safe_literal(f, "'a*/b'"); });
|
||||
ok(out == "CONCAT('a*', '/b')", "quoted literal is split");
|
||||
|
||||
out= capture([](FILE *f)
|
||||
{ dump_put_comment_safe_literal(f, "'ab'"); });
|
||||
ok(out == "'ab'", "literal without a terminator is unchanged");
|
||||
}
|
||||
|
||||
void dump_fatal(const char *msg)
|
||||
{
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(4);
|
||||
}
|
||||
|
||||
int main(int, char **argv)
|
||||
{
|
||||
MY_INIT(argv[0]);
|
||||
plan(NO_PLAN);
|
||||
test_grant();
|
||||
test_revoke();
|
||||
test_create_user();
|
||||
test_emit();
|
||||
my_end(0);
|
||||
return exit_status();
|
||||
}
|
||||
Reference in New Issue
Block a user