6. Auditoria e Backup do Banco de Dados - gentil-eilison/Ankit-Backend GitHub Wiki

6. Auditoria e Logs das Operações no Banco de Dados

A auditoria e registro das operações realizadas dentro do banco de dados foi feita com a biblioteca django-simple-history. Ela foi escolhida por ser bastante poderosa, tendo diversas funcionalidades, como buscar uma instância de um modelo em uma data ou alteração específica, que se integram bem à API utilizada para manipular os modelos do Django, mesmo apresentando alguns pontos negativos.

Dessa forma, a auditoria será responsabilidade total da biblioteca, que realizará todas as operações necessárias para registrar as alterações nos modelos. Essas alterações são as de adição, alteração e remoção de tuplas do banco de dados. Efetivamente, cada tabela que representa uma entidade e que escolhemos fazer o registro das operações terá uma tabela equivalente com o prefixo nomeapp_historicalnomemodelo, que irá armazenar os seguintes atributos:

Nome do atributo Função
history_id É o ID único do registro da alteração
id É o ID da tupla cuja alteração está sendo registrada
history_date Data e hora da alteração
history_change_reason Texto com o porquê da alteração. É opcional, possuindo valor NULL como padrão.
history_type Indica qual operação foi feita. Os valores possíveis são 3: +, indicando adição de uma tupla; -, indicando a remoção dela; e ~, indicando uma alteração.
history_user_id ID do usuário que realizou a alteração

Os atributos descritos na tabela acima estão presentes em todas as tabelas criadas pela biblioteca para o registro. Além deles, são armazenados também todos os atributos dos modelos cuja auditoria está sendo feito naquela tabela. Assim, será possível ter um snapshot do modelo no momento específico daquela operação. As alterações relativas a chaves estrangeiras que não necessitam de uma tabela intermediária também estão incluídas nesses atributos, contendo somente a chave primária da tabela que é referenciada. Entretanto, as relações muitos para muitos terão tabelas próprias para registrar as alterações. Essas tabelas são nomeadas seguindo o seguinte padrão: nomeapp_historicalnomemodelo_nomeatributom2m. Ela conterá os atributos descritos na tabela abaixo:

Nome do Atributo Função
m2m_history_id Identifica a mudança de maneira única para cada par de chave estrangeira atual
id Identifica a mudança no relacionamento como um todo, indicando em qual mudança cada uma das chaves estrangeiras “entrou”.
history_id Identifica em qual mudança da tupla as chaves estrangeiras foram alteradas, referenciando o history_id da tabela histórica do modelo que tem, na sua definição, o campo ManyToManyField. Será igual para todas as tuplas que foram adicionadas ou removidas em uma mesma mudança.

De maneira análoga a como acontece com as tabelas comuns, esses atributos estarão presentes em todas as relações responsáveis por registrar as mudanças nas chaves estrangeiras. Além delas, também são armazenados os ids dos dois modelos que estão se relacionando através dessa chave estrangeira.

Implementação no Código

Para utilizar a django-simple-history, basta, além da configuração básica, que seja criado um atributo e que a ele seja atribuído uma instância do HistoricalRecords. A partir daí, a biblioteca irá fazer todo o trabalho de gerar as tabelas para o registro do histórico e implementar os métodos que garantam que as operações de CRUD sejam registradas nessas tabelas utilizando a API dos models do Django.

No entanto, como o history_user_id possui uma restrição de chave estrangeira, é necessário tratar o caso de quando um usuário que já realizou alterações decide apagar a sua conta do banco de dados. Para isso, foi utilizado um signal do Django, que realiza essa pós-operação nas tabelas de histórico após o usuário ter sido deletado, trocando o usuário para None. O ponto ruim dessa implementação é que, embora os registros do histórico permaneçam, o usuário que as realizou é perdido.

Outro ponto é que, quando alteramos um modelo, o pacote usado para registro de histórico não registra a mudança no modelo que representaria a chave reversa do relacionamento entre as tabelas. Para contornar esse problema, também foi utilizado um signal. Dentro ele, é chamado de maneira explícita o método save() da tabela da chave reversa para que o registro também aconteça nela.

Ferramentas para o Administrador

A django-simple-history também permite que um superusuário administrador consiga visualizar esse histórico de mudanças na interface administrativa do Django, então será possível ter um administrador para verificar esses logs caso haja problema em alguma parte do sistema. Para que o administrador do sistema consiga visualizar esses dados, ele deve estar logado como usuário administrador e entrar na área administrativa da aplicação. Dentro dessa área, ele poderá visualizar o histórico de duas formas: o de uma tupla específica do banco de dados; e o de uma tabela como um todo.

Visualizando o Histórico de uma Tabela Como um Todo

Para visualizar o histórico de uma tabela, basta escolher a opção que possui Historical <Nome-Tabela> dentro da interface administrativa, como mostra a imagem abaixo:

image

Ao selecionar uma delas, o histórico será exibido para o administrador. Na tela do histórico, é possível filtrar por um intervalo de data e hora da alteração, além de filtros específicos para a tabela em questão, caso haja. Além disso, ao detalhar um dos pontos do histórico, é possível ver detalhes importantes, como quem realizou a mudança e o tipo de mudança feita, seja criação, alteração ou modificação.

image

image

Visualizando Histórico de uma Tupla Específica

Para que o administrador consiga ver o histórico de uma tupla em específico, ele deverá entrar na listagem de tuplas da tabela cujos registros ele deseja verificar. Na interface administrativa, isso é feito por meio do clique no link cujo texto é NomedaTabela. Após entrar na listagem das tuplas, ele deve selecionar a tupla cujo histórico é de interesse e clicar, no topo superior direito da página, no botão com o texto "History".

image

Na tela do histórico, ele poderá ver a lista completa dos pontos de modificação da tupla. A listagem conterá informações sobre o objeto em questão; tipo de alteração (criação, modificação, remoção); data e hora; e motivo da mudança.

image

Caso o administrador deseje reverter o objeto, do seu estado atual, para a versão registrada no histórico, ele pode fazer isso seguindo dois simples passos:

  1. Selecionando, dentro da listagem do histórico da tupla, qual versão no tempo para qual ele deseja reverter o objeto;
  2. Clicando no botão "Reverter" no final da página.

image

image

Backup de Dados de Forma Periódica

Ferramenta Escolhida

A implementação do backup do Banco de Dados do Ankit foi realizado utilizando a biblioteca django_dbbackup, que facilita tanto a configuração em relação a onde o usuário quer realizar o backup (AWS, Google Cloud, localmente etc.) quanto a realização do backup, que pode ser personalizada com comandos como limpar velhos arquivos de backup durante sua realização e criptografá-los. No Ankit, o local de armazenamento dos backups escolhido foi uma pasta na raíz do projeto com o nome backups.

Implementação no Código

Para que o backup pudesse ser gerenciado em um painel por contas de administradores e também poder ser implementado de forma periódica e automática, tornou-se necessário ter o auxílio da biblioteca django_celery_beat. A django_dbbackup fornece comandos que podem ser utilizados com o manage.py, como: (1) dbbackup, utilizado para realizar o backup; e o (2) dbrestore, para restaurar um banco utilizando um arquivo de backup. Flags como --clean também podem ser utilizadas para limpar velhos backups.

Utilizando o Celery, foram criadas tarefas no arquivo tasks.py, cada uma contendo comandos tanto para realizar backup do banco de dados quanto da pasta media. Para chamar os comandos, foi utilizada a função call_command() do módulo management do Django, como mostra o código abaixo:

tarefas relacionadas a backup no arquivo tasks.py

Com isso feito, qualquer usuário que possua uma conta de administrador pode agendar ou executar backups no painel de administração do Django.

O Painel de Administração

Executando um Backup

Para executar um backup de forma não automática e única, o administrador, primeiramente, vai ter que ir ao item Clocked da seção Periodic Tasks e clicar no botão Add, para registrar um período de execução de alguma tarefa do Celery (no caso, uma tarefa de backup):

Imagem apontando para o botão de adicionar um período do tipo Clocked

Na tela seguinte, ele será apresentado com dois campos: a data e a hora da execução da tarefa. Ao preenchê-los e clicar no botão SAVE, você irá guardar esse período no banco de dados, como mostra a figura abaixo (tela para qual você será redirecionado(a)):

Tela de listagem de períodos do tipo Clocked

Em seguida, o usuário deve retornar para a página inicial e clicar no botão ADD do item Periodic Tasks para cadastrar uma tarefa do Celery:

Tela de administrador do Django, na sessão principal. Há uma seta vermelha indicando que o usuário deve clicar no botão add de periodic tasks

Quando ele escolher um nome para a tarefa e escolher qual tarefa de backup ele deseja fazer nos campos Name e Task (registered), ele deve ir depois para a seção Schedule e escolher algum Clocked Schedule, que é o tipo de período o qual criamos, e selecionar a data e hora desejada, como mostra a figura abaixo:

Seta apontando para valor do Clocked Schedule escolhido

Depois, ele deve marcar a caixinha ao lado do texto One-off Task, como na imagem abaixo:

Seta apontando para caixa de marcação do item One-off Task

Por fim, é só adicionar a tarefa clicando no botão SAVE. Vale lembrar que é possível excluir essa tarefa de backup depois, já que ela nunca mais vai ser executada; ou criar um novo período do tipo Clocked e editar a tarefa já criada com o novo período. Para realizar a deleção, basta selecionar a(s) tarefa(s) que deseja excluir, ir na caixa do opções de nome Action, escolher delete selected periodic tasks e clicar no botão Go:

Tela mostra passo-a-passo de como realizar uma deleção de tarefa

Após isso, é só clicar no botão de confirmação que irá aparece na página seguinte:

imagem mostrando o botão de confirmação de deleção da tarefa

Automatizando um Backup com Periodicidade

Além do período do tipo Clocked, é possível criar mais 3 tipos de períodos: Crontabs, em que pode-se especificar uma hora ou uma data específica para uma tarefa acontecer; Intervals, que possibilita a adição de um intervalo recorrente (a cada 30 segundos, por exemplo); e Solar Events, que especifica em qual evento solar o backup tem que ser executado, por exemplo. Nas imagens abaixo, encontram-se os campos que precisam ser preenchidos para criar cada um desses tipos de eventos, respectivamente:

Tela de adicionar um crontab

Tela de adicionar um interval

Tela de adicionar um solar event

O Crontab (imagem 2) permite criar um evento único com data e sem hora e vice-versa, que pode se repetir ou não por causa do valor *, diferente do Clocked, que obriga o usuário a colocar uma data e uma hora. Isso significa que você pode usá-lo para criar tarefas que acontecem em um determinado horário do dia todos os dias, por exemplo.

Com um período do tipo Interval como exemplo, que permite colocarmos um certo intervalo para a execução de uma tarefa, como: a cada 30 segundos, a cada hora, a cada dia etc. podemos agora agendar uma tarefa para ser executada a cada 30 segundos. Para isso, o administrador precisa, novamente, ir para a seção de criar uma tarefa. Mas, dessa vez, ele terá que escolher uma periodicidade:

Adicionando uma periodicidade em uma tarefa de backup de banco

Como a periodicidade utilizada como exemplo foi uma do tipo Interval, será no campo Interval Schedule que a periodicidade que criamos irá se encontrar. Após escolhê-la, é só apertar no botão de adicionar novamente (ADD) e sua tarefa estará criada e rodando. A tarefa de backup na imagem abaixo tem o título de "Fazer Backup do Banco"

Tabela com tarefas