Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Como usar as funções que mudaram no MySQL 8.0


A segurança do banco de dados é importante para qualquer configuração do MySQL. Os usuários são a base de qualquer sistema. Em termos de sistemas de banco de dados, geralmente penso neles em dois grupos distintos:


  1. Usuários de aplicativos, serviços ou programas - basicamente clientes ou clientes que usam um serviço.
  2. Desenvolvedores de banco de dados, administradores, analistas, etc… - Aqueles que mantêm, trabalham ou monitoram a infraestrutura de banco de dados.



Embora cada usuário precise acessar o banco de dados em algum nível, essas permissões não são todas iguais.

Por exemplo, clientes e clientes precisam de acesso a seus dados de 'conta de usuário relacionada', mas mesmo isso deve ser monitorado com algum nível de controle. No entanto, algumas tabelas e dados devem estar estritamente fora dos limites (por exemplo, tabelas do sistema).

No entanto:
  • O analista precisa de 'acesso de leitura ', para reunir informações e insights por meio de tabelas de consulta…
  • Os desenvolvedores precisam de várias permissões e privilégios para realizar seu trabalho…
  • Os DBAs precisam de privilégios de 'root' ou similares para executar o programa…
  • Os compradores de um serviço precisam ver o histórico de pedidos e pagamentos…

Você pode imaginar (eu sei que sim) o quão difícil é a tarefa de gerenciar vários usuários ou grupos de usuários em um ecossistema de banco de dados.

Nas versões mais antigas do MySQL, um ambiente de múltiplos usuários é estabelecido de maneira um tanto monótona e repetitiva.

No entanto, a versão 8 implementa um recurso padrão SQL excepcional e poderoso - Roles - que alivia uma das áreas mais redundantes de todo o processo:atribuir privilégios a um usuário.

Então, o que é um papel no MySQL?

Você certamente pode visitar MySQL em 2018:o que há no 8.0 e outras observações, que escrevi para o blog do Variousnines aqui, onde menciono funções para uma visão geral de alto nível. No entanto, onde eu apenas os resumi lá, este post atual procura se aprofundar e se concentrar apenas nos papéis.

Aqui está como a documentação online do MySQL define uma função:"Uma função MySQL é uma coleção nomeada de privilégios".

Essa definição por si só não parece útil?

Mas como?

Veremos nos exemplos a seguir.

Tomar nota dos exemplos fornecidos


Os exemplos incluídos nesta postagem estão em uma estação de trabalho/ambiente de aprendizado e desenvolvimento de 'usuário único' pessoal, portanto, certifique-se de implementar as práticas recomendadas que o beneficiem para suas necessidades ou requisitos específicos. Os nomes de usuário e senhas demonstrados são puramente arbitrários e fracos.

Usuários e privilégios em versões anteriores


No MySQL 5.7, as funções não existem. A atribuição de privilégios aos usuários é feita individualmente. Para entender melhor quais funções fornecem, não vamos usá-las. Isso não faz o menor sentido, eu sei. Mas, à medida que avançamos no post, isso acontecerá.

Abaixo criamos alguns usuários:
CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password'; 
CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password'; 
CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';

Em seguida, esses usuários recebem alguns privilégios:
GRANT SELECT ON some_db.specific_table TO 'reader_1'@'localhost';
GRANT SELECT, INSERT ON some_db.specific_table TO 'reader_writer'@'localhost';
GRANT UPDATE, DELETE ON some_db.specific_table TO 'changer_1'@'localhost';

Ufa, que bom que acabou. Agora voltando para…

E assim, você tem um pedido para implementar mais dois usuários 'somente leitura'…

De volta à prancheta:
CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2'; 
CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';

Atribuindo-lhes privilégios também:
GRANT SELECT ON some_db.specific_table TO 'reader_2'@'localhost';
GRANT ALL ON some_db.specific_table TO 'reader_3'@'localhost';

Você pode ver como isso é menos produtivo, cheio de repetições e propenso a erros? Mas, mais importante, você pegou o erro?

Bom para você!

Ao conceder privilégios para esses dois usuários adicionais, eu acidentalmente concedeu TODOS os privilégios ao novo usuário reader_3.

Ops.

Um erro que qualquer um poderia cometer.

Insira as funções do MySQL


Com funções, muitas das sistemáticas acima atribuição e delegação de privilégios podem ser um pouco simplificadas .

A criação de usuários basicamente permanece a mesma, mas está atribuindo privilégios por meio de funções que diferem:
mysql> CREATE USER 'reader_1'@'localhost' IDENTIFIED BY 'some_password';
Query OK, 0 rows affected (0.19 sec)
mysql> CREATE USER 'reader_writer'@'localhost' IDENTIFIED BY 'another_password';
Query OK, 0 rows affected (0.22 sec)
mysql> CREATE USER 'changer_1'@'localhost' IDENTIFIED BY 'a_password';
Query OK, 0 rows affected (0.08 sec)
mysql> CREATE USER 'reader_2'@'localhost' IDENTIFIED BY 'password_2';
Query OK, 0 rows affected (0.28 sec)
mysql> CREATE USER 'reader_3'@'localhost' IDENTIFIED BY 'password_3';
Query OK, 0 rows affected (0.12 sec)

Consultando a tabela do sistema mysql.user, você pode ver que esses usuários recém-criados existem:

(Observação:tenho várias contas de usuário neste ambiente de aprendizado/desenvolvimento e suprimi grande parte da saída para melhor clareza na tela.)
mysql> SELECT User FROM mysql.user;
+------------------+
| User             |
+------------------+
| changer_1        |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| reader_1         |
| reader_2         |
| reader_3         |
| reader_writer    |
| root             |
|                  | --multiple rows remaining here...
+------------------+
23 rows in set (0.00 sec)

Eu tenho esta tabela arbitrária e dados de amostra:
mysql> SELECT * FROM name;
+--------+------------+
| f_name | l_name     |
+--------+------------+
| Jim    | Dandy      |
| Johhny | Applesauce |
| Ashley | Zerro      |
| Ashton | Zerra      |
| Ashmon | Zerro      |
+--------+------------+
5 rows in set (0.00 sec)

Vamos agora usar funções para estabelecer e atribuir privilégios para os novos usuários usarem a tabela de nomes.

Primeiro, crie os papéis:
mysql> CREATE ROLE main_read_only;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_read_write;
Query OK, 0 rows affected (0.11 sec)
mysql> CREATE ROLE main_changer;
Query OK, 0 rows affected (0.14 sec)

Observe a tabela mysql.user novamente:
mysql> SELECT User FROM mysql.user;
+------------------+
| User             |
+------------------+
| main_changer     |
| main_read_only   |
| main_read_write  |
| changer_1        |
| mysql.infoschema |
| mysql.session    |
| mysql.sys        |
| reader_1         |
| reader_2         |
| reader_3         |
| reader_writer    |
| root             |
|                  |
+------------------+
26 rows in set (0.00 sec)

Com base nessa saída, podemos supor; que, em toda a essência, os papéis são, na verdade, os próprios usuários.

Em seguida, atribuição de privilégios:
mysql> GRANT SELECT ON practice.name TO 'main_read_only';
Query OK, 0 rows affected (0.14 sec)
mysql> GRANT SELECT, INSERT ON practice.name TO 'main_read_write';
Query OK, 0 rows affected (0.07 sec)
mysql> GRANT UPDATE, DELETE ON practice.name TO 'main_changer';
Query OK, 0 rows affected (0.16 sec)

Um breve interlúdio


Espere um minuto. Posso apenas fazer login e realizar qualquer tarefa com as próprias contas de função? Afinal, eles são usuários e têm os privilégios necessários.

Vamos tentar fazer login no banco de dados de prática com a função main_changer:
:~$ mysql -u main_changer -p practice
Enter password: 
ERROR 1045 (28000): Access denied for user 'main_changer'@'localhost' (using password: YES

O simples fato de recebermos um prompt de senha é uma boa indicação de que não podemos (pelo menos neste momento). Como você se lembra, não defini uma senha para nenhuma das funções durante sua criação.

O que a coluna authentication_string das tabelas do sistema mysql.user tem a dizer?
mysql> SELECT User, authentication_string, password_expired
    -> FROM mysql.user
    -> WHERE User IN ('main_read_only', 'root', 'main_read_write', 'main_changer')\G
*************************** 1. row ***************************
                 User: main_changer
authentication_string: 
     password_expired: Y
*************************** 2. row ***************************
                 User: main_read_only
authentication_string: 
     password_expired: Y
*************************** 3. row ***************************
                 User: main_read_write
authentication_string: 
     password_expired: Y
*************************** 4. row ***************************
                 User: root
authentication_string: ***various_jumbled_mess_here*&&*&*&*##
     password_expired: N
4 rows in set (0.00 sec)

Incluí o usuário root entre os nomes de função para a verificação de predicado IN() para simplesmente demonstrar que ele tem uma authentication_string, onde as funções não.

Esta passagem na documentação CREATE ROLE esclarece bem:"Uma função quando criada é bloqueada, não tem senha e recebe o plug-in de autenticação padrão. (Esses atributos de função podem ser alterados posteriormente com a instrução ALTER USER, por usuários que têm o privilégio global CREATE USER.)"

De volta à tarefa em questão, agora podemos atribuir as funções aos usuários com base em seus níveis de privilégios necessários.

Observe que nenhuma cláusula ON está presente no comando:
mysql> GRANT 'main_read_only' TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.13 sec)
mysql> GRANT 'main_read_write' TO 'reader_writer'@'localhost';
Query OK, 0 rows affected (0.16 sec)
mysql> GRANT 'main_changer', 'main_read_only' TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.13 sec)

Pode ser menos confuso se você usar algum tipo de 'convenção de nomenclatura ' ao estabelecer nomes de função, (não sei se o MySQL fornece um neste momento ... Comunidade?) se por nenhuma outra razão a não ser diferenciar visualmente entre eles e usuários 'não-função' regulares.

Ainda há trabalho a ser feito


Isso foi super fácil, não foi?

Menos redundante que a maneira antiga de atribuição de privilégios.

Vamos colocar esses usuários para trabalhar agora.

Podemos ver os privilégios concedidos para um usuário com a sintaxe SHOW GRANTS. Aqui está o que está atualmente atribuído à conta de usuário reader_1:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+------------------------------------------------------+
| Grants for [email protected]                        |
+------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost`         |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost` |
+------------------------------------------------------+
2 rows in set (0.02 sec)

Embora isso forneça uma saída informativa, você pode 'ajustar ' a instrução para obter informações ainda mais granulares sobre quaisquer privilégios exatos que uma função atribuída fornece, incluindo uma cláusula USING na instrução SHOW GRANTS e nomeando as funções atribuídas como nome:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost' USING 'main_read_only';
+-------------------------------------------------------------+
| Grants for [email protected]                               |
+-------------------------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost`                |
| GRANT SELECT ON `practice`.`name` TO `reader_1`@`localhost` |
| GRANT `main_read_only`@`%` TO `reader_1`@`localhost`        |
+-------------------------------------------------------------+
3 rows in set (0.00 sec)

Depois de fazer login com reader_1:
mysql> SELECT * FROM practice.name;
ERROR 1142 (42000): SELECT command denied to user 'reader_1'@'localhost' for table 'name'

O que na Terra? Esse usuário recebeu privilégios SELECT por meio da função main_read_only.

Para investigar, vamos visitar 2 novas tabelas na versão 8, especificamente para funções.

A tabela mysql.role_edges mostra quais funções foram concedidas a qualquer usuário:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER       | TO_HOST   | TO_USER       | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| %         | main_changer    | localhost | changer_1     | N                 |
| %         | main_read_only  | localhost | changer_1     | N                 |
| %         | main_read_only  | localhost | reader_1      | N                 |
| %         | main_read_only  | localhost | reader_2      | N                 |
| %         | main_read_only  | localhost | reader_3      | N                 |
| %         | main_read_write | localhost | reader_writer | N                 |
+-----------+-----------------+-----------+---------------+-------------------+
6 rows in set (0.00 sec)

Mas, sinto que a outra tabela adicional, mysql.default_roles, nos ajudará a resolver melhor os problemas de SELECT para o usuário reader_1:
mysql> DESC mysql.default_roles;
+-------------------+----------+------+-----+---------+-------+
| Field             | Type     | Null | Key | Default | Extra |
+-------------------+----------+------+-----+---------+-------+
| HOST              | char(60) | NO   | PRI |         |       |
| USER              | char(32) | NO   | PRI |         |       |
| DEFAULT_ROLE_HOST | char(60) | NO   | PRI | %       |       |
| DEFAULT_ROLE_USER | char(32) | NO   | PRI |         |       |
+-------------------+----------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> SELECT * FROM mysql.default_roles;
Empty set (0.00 sec)

Conjunto de resultados vazio.

Acontece que, para que um usuário possa usar uma função - e, finalmente, os privilégios - o usuário deve receber uma função padrão.
mysql> SET DEFAULT ROLE main_read_only TO 'reader_1'@'localhost', 'reader_2'@'localhost', 'reader_3'@'localhost';
Query OK, 0 rows affected (0.11 sec)

(Uma função padrão pode ser atribuída a vários usuários em um comando como acima…)
mysql> SET DEFAULT ROLE main_read_only, main_changer TO 'changer_1'@'localhost';
Query OK, 0 rows affected (0.10 sec)

(Um usuário pode ter várias funções padrão especificadas como no caso do usuário changer_1…)

O usuário leitor_1 agora está conectado...
mysql> SELECT CURRENT_USER();
+--------------------+
| CURRENT_USER()     |
+--------------------+
| [email protected] |
+--------------------+
1 row in set (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE()       |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.03 sec)

Podemos ver a função atualmente ativa e também que o leitor_1 pode emitir comandos SELECT agora:
mysql> SELECT * FROM practice.name;
+--------+------------+
| f_name | l_name     |
+--------+------------+
| Jim    | Dandy      |
| Johhny | Applesauce |
| Ashley | Zerro      |
| Ashton | Zerra      |
| Ashmon | Zerro      |
+--------+------------+
5 rows in set (0.00 sec)

Outras nuances ocultas


Há outra parte importante do quebra-cabeça precisamos entender.

Existem potencialmente 3 'níveis' ou 'variantes' diferentes de atribuição de função:
SET ROLE …;
SET DEFAULT ROLE …;
SET ROLE DEFAULT …;

Eu concedo uma função adicional ao usuário reader_1 e, em seguida, faço login com esse usuário (não mostrado):
mysql> GRANT 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)

Como a função main_read_write tem o privilégio INSERT, o usuário reader_1 agora pode executar esse comando, certo?
mysql> INSERT INTO name(f_name, l_name)
    -> VALUES('Josh', 'Otwell');
ERROR 1142 (42000): INSERT command denied to user 'reader_1'@'localhost' for table 'name'

O que está acontecendo aqui?

Isso pode ajudar...
mysql> SELECT CURRENT_ROLE();
+----------------------+
| CURRENT_ROLE()       |
+----------------------+
| `main_read_only`@`%` |
+----------------------+
1 row in set (0.00 sec)

Lembre-se de que inicialmente definimos o usuário reader_1 como uma função padrão de main_read_only. É aqui que precisamos usar um desses 'níveis' distintos do que termo vagamente 'definição de função':
mysql> SET ROLE main_read_write;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE()        |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)

Agora tente esse INSERT novamente:
mysql> INSERT INTO name(f_name, l_name)
    -> VALUES('Josh', 'Otwell');
Query OK, 1 row affected (0.12 sec)

No entanto, uma vez que o usuário reader_1 desconectar, a função main_read_write não estará mais ativa quando reader_1 efetuar login novamente. Embora o usuário reader_1 tenha a função main_read_write concedida a ele, ela não é o padrão.

Vamos agora conhecer o 3º 'nível' de 'definição de função', SET ROLE DEFAULT.

Suponha que o usuário reader_1 ainda não tenha funções atribuídas:
mysql> SHOW GRANTS FOR 'reader_1'@'localhost';
+----------------------------------------------+
| Grants for [email protected]                |
+----------------------------------------------+
| GRANT USAGE ON *.* TO `reader_1`@`localhost` |
+----------------------------------------------+
1 row in set (0.00 sec)

Vamos CONCEDER a este usuário 2 funções:
mysql> GRANT 'main_changer', 'main_read_write' TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.07 sec)

Atribua uma função padrão:
mysql> SET DEFAULT ROLE ‘main_changer’ TO 'reader_1'@'localhost';
Query OK, 0 rows affected (0.17 sec)

Em seguida, com o usuário reader_1 conectado, essa função padrão está ativa:
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE()     |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)

Agora mude para a função main_read_write:
mysql> SET ROLE 'main_read_write';
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT CURRENT_ROLE();
+-----------------------+
| CURRENT_ROLE()        |
+-----------------------+
| `main_read_write`@`%` |
+-----------------------+
1 row in set (0.00 sec)

Mas, para retornar à função padrão atribuída, use SET ROLE DEFAULT conforme mostrado abaixo:
mysql> SET ROLE DEFAULT;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CURRENT_ROLE();
+--------------------+
| CURRENT_ROLE()     |
+--------------------+
| `main_changer`@`%` |
+--------------------+
1 row in set (0.00 sec)

Funções não concedidas


Mesmo que o usuário changer_1 tenha 2 funções disponíveis durante uma sessão:
mysql> SELECT CURRENT_ROLE();
+-----------------------------------------+
| CURRENT_ROLE()                          |
+-----------------------------------------+
| `main_changer`@`%`,`main_read_only`@`%` |
+-----------------------------------------+
1 row in set (0.00 sec)

O que acontece se você tentar definir um usuário para uma função que não foi concedida?
mysql> SET ROLE main_read_write;
ERROR 3530 (HY000): `main_read_write`@`%` is not granted to `changer_1`@`localhost`

Negado.

Tira embora


Nenhum sistema de gerenciamento de usuários estaria completo sem a capacidade de restringir ou até mesmo remover o acesso a determinadas operações, caso haja necessidade.

Temos o comando SQL REVOKE à nossa disposição para remover privilégios de usuários e funções.

Lembre-se de que o papel main_changer tem esse conjunto de privilégios, essencialmente, todos os usuários que receberam esse papel também:
mysql> SHOW GRANTS FOR main_changer;
+-----------------------------------------------------------------+
| Grants for [email protected]%                                       |
+-----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%`                        |
| GRANT UPDATE, DELETE ON `practice`.`name` TO `main_changer`@`%` |
+-----------------------------------------------------------------+
2 rows in set (0.00 sec)
mysql> REVOKE DELETE ON practice.name FROM 'main_changer';
Query OK, 0 rows affected (0.11 sec)
mysql> SHOW GRANTS FOR main_changer;
+---------------------------------------------------------+
| Grants for [email protected]%                               |
+---------------------------------------------------------+
| GRANT USAGE ON *.* TO `main_changer`@`%`                |
| GRANT UPDATE ON `practice`.`name` TO `main_changer`@`%` |
+---------------------------------------------------------+
2 rows in set (0.00 sec)

Para saber quais usuários essa mudança afetou, podemos visitar a tabela mysql.role_edges novamente:
mysql> SELECT * FROM mysql.role_edges WHERE FROM_USER = 'main_changer';
+-----------+--------------+-----------+-----------+-------------------+
| FROM_HOST | FROM_USER    | TO_HOST   | TO_USER   | WITH_ADMIN_OPTION |
+-----------+--------------+-----------+-----------+-------------------+
| %         | main_changer | localhost | changer_1 | N                 |
+-----------+--------------+-----------+-----------+-------------------+
1 row in set (0.00 sec)

E podemos ver que o usuário changer_1 não tem mais o privilégio DELETE:
mysql> SHOW GRANTS FOR 'changer_1'@'localhost' USING 'main_changer';
+--------------------------------------------------------------------------+
| Grants for [email protected]                                           |
+--------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `changer_1`@`localhost`                            |
| GRANT UPDATE ON `practice`.`name` TO `changer_1`@`localhost`             |
| GRANT `main_changer`@`%`,`main_read_only`@`%` TO `changer_1`@`localhost` |
+--------------------------------------------------------------------------+
3 rows in set (0.00 sec)

Por fim, se precisarmos nos livrar completamente de um papel, temos o comando DROP ROLE para isso:
mysql> DROP ROLE main_read_only;
Query OK, 0 rows affected (0.17 sec)

E consultando a tabela mysql.role_edges, o papel main_read_only foi removido:
mysql> SELECT * FROM mysql.role_edges;
+-----------+-----------------+-----------+---------------+-------------------+
| FROM_HOST | FROM_USER       | TO_HOST   | TO_USER       | WITH_ADMIN_OPTION |
+-----------+-----------------+-----------+---------------+-------------------+
| %         | main_changer    | localhost | changer_1     | N                 |
| %         | main_read_write | localhost | reader_1      | N                 |
| %         | main_read_write | localhost | reader_writer | N                 |
+-----------+-----------------+-----------+---------------+-------------------+
3 rows in set (0.00 sec)

(Bônus:este fantástico vídeo do YouTube foi um ótimo recurso de aprendizado para mim sobre Funções.)

Este exemplo de criação de usuário, atribuição de função e configuração é, na melhor das hipóteses, rudimentar. No entanto, os papéis têm seu próprio conjunto de regras que os tornam longe de ser triviais. Minha esperança é que, por meio desta postagem no blog, eu tenha esclarecido as áreas que são menos intuitivas do que outras, permitindo que os leitores entendam melhor os usos potenciais de funções em seus sistemas.

Obrigado por ler.