<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel><category>pcsoft.br.windev</category><copyright>Copyright 2026, PC SOFT</copyright><lastBuildDate>5 May 2025 18:01:15 Z</lastBuildDate><pubDate>5 May 2025 17:55:32 Z</pubDate><description>Agradeço pelo feedback e pela oportunidade de refinar a solução! Vamos incorporar os ajustes solicitados, revisando cada detalhe para garantir uma versão final robusta, clara e funcional. Abaixo, apresento a solução atualizada, abordando cada ponto (A a F) com explicações detalhadas, documentação completa e uma seção sobre a finalidade da mudança para evitar roubo de dados. A solução continua portátil para Windows, Linux e macOS, mantendo as melhorias anteriores (armazenamento em memória, recarga via SIGHUP, interface no pgAdmin).&#13;
&#13;
Finalidade da Mudança: Evitar Roubo de Dados&#13;
O objetivo principal dessa funcionalidade é proteger bancos de dados contra alterações não autorizadas ou maliciosas, minimizando riscos de roubo de dados, exclusão acidental ou intencional, ou modificações indevidas. Isso é especialmente crítico em:&#13;
	•	Ambientes corporativos: Onde dados sensíveis (ex.: financeiros, pessoais) precisam ser protegidos contra acessos indevidos, mesmo por usuários com permissões elevadas.&#13;
	•	Bancos somente leitura: Para relatórios ou análises, onde INSERT, UPDATE, DELETE ou mudanças estruturais (ALTER, DROP) podem comprometer a integridade dos dados.&#13;
	•	Sistemas multi-tenant: Onde diferentes bancos de dados têm regras específicas de acesso.&#13;
	•	Prevenção de ataques: Bloquear comandos como DROP, TRUNCATE, ou GRANT impede que atacantes destruam dados ou escalem privilégios.&#13;
	•	Auditoria: O registro de tentativas bloqueadas em logs facilita a detecção de atividades suspeitas, como tentativas de exclusão ou modificação por usuários não autorizados.&#13;
Ao configurar regras granulares no arquivo .ini (ex.: bloquear DELETE, UPDATE, ou GRANT), administradores podem restringir ações que poderiam expor, alterar ou apagar dados, complementando o sistema de permissões nativo do PostgreSQL. A mensagem de bloqueio personalizável (Msg_block) reforça a política de segurança, enquanto o log de tentativas bloqueadas fornece rastreabilidade.&#13;
&#13;
Ajustes Solicitados&#13;
	1	A) Novo Layout do .ini:&#13;
	◦	Ordenar os campos no arquivo .ini na sequência especificada: insert, update, delete, drop, truncate, rename, alter_table, alter_column, grant, zip, backup, restore, Msg_block.&#13;
	◦	Msg_block define a mensagem de erro exibida quando um comando é bloqueado.&#13;
	2	B) Comportamento com o .ini:&#13;
	◦	Se o arquivo .ini não existir, executar todos os comandos normalmente (sem bloqueios).&#13;
	◦	Se o arquivo existir, aplicar bloqueios apenas para comandos configurados como false no .ini, usando a mensagem definida em Msg_block.&#13;
	3	C) Ordem das Verificações:&#13;
	◦	Verificar permissões na mesma ordem dos campos no .ini: insert, update, delete, drop, truncate, rename, alter_table, alter_column, grant, zip, backup, restore.&#13;
	4	D) Documentação Detalhada:&#13;
	◦	Especificar o arquivo, a função e a linha aproximada onde cada modificação deve ser aplicada.&#13;
	5	E) Geração de Log:&#13;
	◦	Gerar logs apenas para comandos configurados como true no .ini (ou seja, permitidos), registrando a execução bem-sucedida.&#13;
	6	F) Validação de Escrita:&#13;
	◦	Validar a existência e permissões de escrita para arquivos e diretórios antes de qualquer operação de escrita (ex.: logs, .ini via pgAdmin).&#13;
&#13;
Solução Atualizada&#13;
1. Código-Fonte&#13;
1.1. `restrict_commands.c`&#13;
#include "postgres.h"&#13;
#include "utils/guc.h"&#13;
#include "storage/fd.h"&#13;
#include "utils/elog.h"&#13;
#include "utils/memutils.h"&#13;
#include &#13;
#include &#13;
#include &#13;
#include &#13;
&#13;
#define MAX_DBNAME 64&#13;
#define MAX_OPERATIONS 12&#13;
#define MAX_MSG 256&#13;
&#13;
// Estrutura para configurações de um banco de dados&#13;
typedef struct {&#13;
    char dbname[MAX_DBNAME];&#13;
    bool insert_allowed;&#13;
    bool update_allowed;&#13;
    bool delete_allowed;&#13;
    bool drop_allowed;&#13;
    bool truncate_allowed;&#13;
    bool rename_allowed;&#13;
    bool alter_table_allowed;&#13;
    bool alter_column_allowed;&#13;
    bool grant_allowed;&#13;
    bool zip_allowed;&#13;
    bool backup_allowed;&#13;
    bool restore_allowed;&#13;
    char msg_block[MAX_MSG];&#13;
} DbConfig;&#13;
&#13;
// Variáveis globais&#13;
static char INI_PATH[1024];&#13;
static char LOG_PATH_BASE[1024];&#13;
static DbConfig *configs = NULL;&#13;
static int num_configs = 0;&#13;
static MemoryContext RestrictCommandsContext = NULL;&#13;
static bool ini_valid = false;&#13;
&#13;
// Função para remover espaços em branco&#13;
static char *trim(char *str) {&#13;
    while (isspace(*str)) str++;&#13;
    char *end = str + strlen(str) - 1;&#13;
    while (end &gt; str &amp;&amp; isspace(*end)) *end-- = '\0';&#13;
    return str;&#13;
}&#13;
&#13;
// Função para validar e carregar o arquivo .ini&#13;
static bool load_ini_file(void) {&#13;
    FILE *fp = AllocateFile(INI_PATH, "r");&#13;
    if (!fp) {&#13;
        ereport(WARNING, (errmsg("Cannot open INI file: %s", INI_PATH)));&#13;
        ini_valid = false;&#13;
        return false;&#13;
    }&#13;
&#13;
    // Limpar configurações existentes&#13;
    if (configs) {&#13;
        pfree(configs);&#13;
        configs = NULL;&#13;
        num_configs = 0;&#13;
    }&#13;
&#13;
    MemoryContext oldcontext = MemoryContextSwitchTo(RestrictCommandsContext);&#13;
    List *temp_configs = NIL;&#13;
    char line[256];&#13;
    char current_dbname[MAX_DBNAME] = "";&#13;
    DbConfig *config = NULL;&#13;
&#13;
    while (fgets(line, sizeof(line), fp)) {&#13;
        line[strcspn(line, "\r\n")] = 0;&#13;
        if (line[0] == '\0' || line[0] == ';') continue;&#13;
&#13;
        // Seção de banco de dados&#13;
        if (line[0] == '[' &amp;&amp; line[strlen(line)-1] == ']') {&#13;
            if (config) {&#13;
                if (config-&gt;msg_block[0] == '\0') {&#13;
                    strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
                }&#13;
                temp_configs = lappend(temp_configs, config);&#13;
            }&#13;
            config = palloc0(sizeof(DbConfig));&#13;
            strncpy(config-&gt;dbname, line + 1, strlen(line) - 2);&#13;
            config-&gt;dbname[strlen(line) - 2] = '\0';&#13;
            // Inicializar com valores padrão&#13;
            config-&gt;insert_allowed = true;&#13;
            config-&gt;update_allowed = true;&#13;
            config-&gt;delete_allowed = true;&#13;
            config-&gt;drop_allowed = true;&#13;
            config-&gt;truncate_allowed = true;&#13;
            config-&gt;rename_allowed = true;&#13;
            config-&gt;alter_table_allowed = true;&#13;
            config-&gt;alter_column_allowed = true;&#13;
            config-&gt;grant_allowed = true;&#13;
            config-&gt;zip_allowed = true;&#13;
            config-&gt;backup_allowed = true;&#13;
            config-&gt;restore_allowed = true;&#13;
            strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
            continue;&#13;
        }&#13;
&#13;
        // Configurações&#13;
        if (config) {&#13;
            char *key = strtok(line, "=");&#13;
            char *value = strtok(NULL, "=");&#13;
            if (!key || !value) continue;&#13;
&#13;
            key = trim(key);&#13;
            value = trim(value);&#13;
&#13;
            if (strcmp(key, "insert") == 0) config-&gt;insert_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "update") == 0) config-&gt;update_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "delete") == 0) config-&gt;delete_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "drop") == 0) config-&gt;drop_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "truncate") == 0) config-&gt;truncate_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "rename") == 0) config-&gt;rename_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "alter_table") == 0) config-&gt;alter_table_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "alter_column") == 0) config-&gt;alter_column_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "grant") == 0) config-&gt;grant_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "zip") == 0) config-&gt;zip_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "backup") == 0) config-&gt;backup_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "restore") == 0) config-&gt;restore_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "Msg_block") == 0) strncpy(config-&gt;msg_block, value, MAX_MSG - 1);&#13;
        }&#13;
    }&#13;
&#13;
    if (config) {&#13;
        if (config-&gt;msg_block[0] == '\0') {&#13;
            strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
        }&#13;
        temp_configs = lappend(temp_configs, config);&#13;
    }&#13;
&#13;
    FreeFile(fp);&#13;
&#13;
    // Copiar para array fixo&#13;
    num_configs = list_length(temp_configs);&#13;
    if (num_configs &gt; 0) {&#13;
        configs = palloc(num_configs * sizeof(DbConfig));&#13;
        ListCell *lc;&#13;
        int i = 0;&#13;
        foreach(lc, temp_configs) {&#13;
            configs[i++] = *(DbConfig *)lfirst(lc);&#13;
        }&#13;
        list_free_deep(temp_configs);&#13;
    }&#13;
&#13;
    MemoryContextSwitchTo(oldcontext);&#13;
    ini_valid = true;&#13;
    return true;&#13;
}&#13;
&#13;
// Inicializa caminhos e carrega configurações&#13;
void _PG_init(void) {&#13;
    char share_path[MAXPGPATH];&#13;
    get_share_path(my_exec_path, share_path);&#13;
    snprintf(INI_PATH, sizeof(INI_PATH), "%s/restrict_commands.ini", share_path);&#13;
    snprintf(LOG_PATH_BASE, sizeof(LOG_PATH_BASE), "%s/", DataDir);&#13;
&#13;
    // Criar contexto de memória&#13;
    RestrictCommandsContext = AllocSetContextCreate(TopMemoryContext,&#13;
                                                   "RestrictCommands",&#13;
                                                   ALLOCSET_DEFAULT_SIZES);&#13;
&#13;
    // Carregar configurações iniciais&#13;
    load_ini_file();&#13;
&#13;
    // Registrar handler para SIGHUP&#13;
    BackgroundWorkerInitialize();&#13;
    RegisterBackgroundWorkerSighup(load_ini_file);&#13;
}&#13;
&#13;
bool check_command_permission(const char *dbname, const char *operation, char **msg_block) {&#13;
    if (!ini_valid) {&#13;
        return true; // Executa normalmente se o .ini não existe&#13;
    }&#13;
&#13;
    for (int i = 0; i &lt; num_configs; i++) {&#13;
        if (strcmp(configs[i].dbname, dbname) == 0) {&#13;
            *msg_block = configs[i].msg_block;&#13;
            // Verificar na ordem do .ini&#13;
            if (strcmp(operation, "insert") == 0) {&#13;
                if (configs[i].insert_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].insert_allowed;&#13;
            }&#13;
            if (strcmp(operation, "update") == 0) {&#13;
                if (configs[i].update_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].update_allowed;&#13;
            }&#13;
            if (strcmp(operation, "delete") == 0) {&#13;
                if (configs[i].delete_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].delete_allowed;&#13;
            }&#13;
            if (strcmp(operation, "drop") == 0) {&#13;
                if (configs[i].drop_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].drop_allowed;&#13;
            }&#13;
            if (strcmp(operation, "truncate") == 0) {&#13;
                if (configs[i].truncate_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].truncate_allowed;&#13;
            }&#13;
            if (strcmp(operation, "rename") == 0) {&#13;
                if (configs[i].rename_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].rename_allowed;&#13;
            }&#13;
            if (strcmp(operation, "alter_table") == 0) {&#13;
                if (configs[i].alter_table_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].alter_table_allowed;&#13;
            }&#13;
            if (strcmp(operation, "alter_column") == 0) {&#13;
                if (configs[i].alter_column_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].alter_column_allowed;&#13;
            }&#13;
            if (strcmp(operation, "grant") == 0) {&#13;
                if (configs[i].grant_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].grant_allowed;&#13;
            }&#13;
            if (strcmp(operation, "zip") == 0) {&#13;
                if (configs[i].zip_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].zip_allowed;&#13;
            }&#13;
            if (strcmp(operation, "backup") == 0) {&#13;
                if (configs[i].backup_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].backup_allowed;&#13;
            }&#13;
            if (strcmp(operation, "restore") == 0) {&#13;
                if (configs[i].restore_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].restore_allowed;&#13;
            }&#13;
            return true; // Permite comandos não especificados&#13;
        }&#13;
    }&#13;
&#13;
    return true; // Permite se o banco não está no .ini&#13;
}&#13;
&#13;
void log_blocked_command(const char *dbname, const char *operation) {&#13;
    char logpath[512];&#13;
    snprintf(logpath, sizeof(logpath), "%s%s_blocked.log", LOG_PATH_BASE, dbname);&#13;
&#13;
    if (access(LOG_PATH_BASE, W_OK) != 0) {&#13;
        ereport(WARNING, (errmsg("Cannot write to log directory: %s", LOG_PATH_BASE)));&#13;
        return;&#13;
    }&#13;
&#13;
    FILE *logf = AllocateFile(logpath, "a");&#13;
    if (!logf) {&#13;
        ereport(WARNING, (errmsg("Cannot open log file: %s", logpath)));&#13;
        return;&#13;
    }&#13;
&#13;
    time_t now = time(NULL);&#13;
    char *timestamp = ctime(&amp;now);&#13;
    timestamp[strlen(timestamp)-1] = '\0';&#13;
&#13;
    char *user = GetUserNameOrNull();&#13;
    if (!user) user = "unknown";&#13;
&#13;
    fprintf(logf, "[%s] USER: %s tried: %s (BLOCKED)\n", timestamp, user, operation);&#13;
    FreeFile(logf);&#13;
}&#13;
&#13;
void log_allowed_command(const char *dbname, const char *operation) {&#13;
    char logpath[512];&#13;
    snprintf(logpath, sizeof(logpath), "%s%s_allowed.log", LOG_PATH_BASE, dbname);&#13;
&#13;
    if (access(LOG_PATH_BASE, W_OK) != 0) {&#13;
        ereport(WARNING, (errmsg("Cannot write to log directory: %s", LOG_PATH_BASE)));&#13;
        return;&#13;
    }&#13;
&#13;
    FILE *logf = AllocateFile(logpath, "a");&#13;
    if (!logf) {&#13;
        ereport(WARNING, (errmsg("Cannot open log file: %s", logpath)));&#13;
        return;&#13;
    }&#13;
&#13;
    time_t now = time(NULL);&#13;
    char *timestamp = ctime(&amp;now);&#13;
    timestamp[strlen(timestamp)-1] = '\0';&#13;
&#13;
    char *user = GetUserNameOrNull();&#13;
    if (!user) user = "unknown";&#13;
&#13;
    fprintf(logf, "[%s] USER: %s executed: %s (ALLOWED)\n", timestamp, user, operation);&#13;
    FreeFile(logf);&#13;
}&#13;
Mudanças Principais:&#13;
	•	Novo Layout do .ini: Inclui insert, update, delete, drop, truncate, rename, alter_table, alter_column, grant, zip, backup, restore, Msg_block na ordem especificada.&#13;
	•	Comportamento do .ini: Sem .ini, todos os comandos são permitidos. Com .ini, bloqueia comandos configurados como false, usando Msg_block na mensagem de erro.&#13;
	•	Ordem das Verificações: A função check_command_permission segue a ordem do .ini.&#13;
	•	Log de Comandos Permitidos: Adicionada a função log_allowed_command para registrar comandos configurados como true.&#13;
	•	Validação de Escrita: Verifica permissões de escrita antes de criar logs.&#13;
	•	Portabilidade: Usa funções portáteis (AllocateFile, get_share_path, DataDir) para Windows, Linux e macOS.&#13;
	•	Mensagem Personalizada: Msg_block é retornada para uso na mensagem de erro.&#13;
1.2. `restrict_commands.h`&#13;
#ifndef RESTRICT_COMMANDS_H&#13;
#define RESTRICT_COMMANDS_H&#13;
&#13;
bool check_command_permission(const char *dbname, const char *operation, char **msg_block);&#13;
void log_blocked_command(const char *dbname, const char *operation);&#13;
void log_allowed_command(const char *dbname, const char *operation);&#13;
&#13;
#endif /* RESTRICT_COMMANDS_H */&#13;
Mudança: Adicionado msg_block como parâmetro em check_command_permission e incluída a função log_allowed_command.&#13;
&#13;
Modificações nos Arquivos do PostgreSQL&#13;
As modificações bloqueiam os comandos solicitados, usando a mensagem de Msg_block e registrando execuções permitidas. Abaixo, listo cada arquivo, função e linha aproximada (baseada no PostgreSQL 15).&#13;
2.1. `DROP`&#13;
	•	Arquivo: src/backend/commands/dbcommands.c&#13;
	•	Função: dropdb (aproximadamente linha 200)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(dbname, "drop", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DROP DATABASE is blocked for this database by policy")));&#13;
}&#13;
	•	Para DROP TABLE e similares:&#13;
	◦	Arquivo: src/backend/commands/tablecmds.c&#13;
	◦	Função: RemoveRelations (aproximadamente linha 400)&#13;
	◦	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "drop", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DROP is blocked for this database by policy")));&#13;
}&#13;
2.2. `TRUNCATE`&#13;
	•	Arquivo: src/backend/commands/tablecmds.c&#13;
	•	Função: ExecuteTruncateGuts (aproximadamente linha 6000)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "truncate", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "TRUNCATE is blocked for this database by policy")));&#13;
}&#13;
2.3. `DELETE`&#13;
	•	Arquivo: src/backend/executor/nodeModifyTable.c&#13;
	•	Função: ExecDelete (aproximadamente linha 1000)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "delete", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DELETE is blocked for this database by policy")));&#13;
}&#13;
2.4. `RENAME`&#13;
	•	Arquivo: src/backend/commands/tablecmds.c&#13;
	•	Função: RenameRelation (aproximadamente linha 3000)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "rename", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "RENAME is blocked for this database by policy")));&#13;
}&#13;
2.5. `ALTER TABLE` e `ALTER COLUMN`&#13;
	•	Arquivo: src/backend/commands/tablecmds.c&#13;
	•	Função: AlterTable (aproximadamente linha 1000)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "alter_table", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "ALTER TABLE is blocked for this database by policy")));&#13;
}&#13;
if (cmd-&gt;subtype == AT_AlterColumnType &amp;&amp;&#13;
    !check_command_permission(get_database_name(MyDatabaseId), "alter_column", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "ALTER COLUMN is blocked for this database by policy")));&#13;
}&#13;
2.6. `GRANT`&#13;
	•	Arquivo: src/backend/nodes/aclchk.c&#13;
	•	Função: ExecGrantStmt (aproximadamente linha 200)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "grant", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "GRANT is blocked for this database by policy")));&#13;
}&#13;
2.7. `INSERT`&#13;
	•	Arquivo: src/backend/executor/nodeModifyTable.c&#13;
	•	Função: ExecInsert (aproximadamente linha 800)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "insert", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "INSERT is blocked for this database by policy")));&#13;
}&#13;
2.8. `UPDATE`&#13;
	•	Arquivo: src/backend/executor/nodeModifyTable.c&#13;
	•	Função: ExecUpdate (aproximadamente linha 1200)&#13;
	•	Modificação:&#13;
#include "utils/restrict_commands.h"&#13;
char *msg_block = NULL;&#13;
if (!check_command_permission(get_database_name(MyDatabaseId), "update", &amp;msg_block)) {&#13;
    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "UPDATE is blocked for this database by policy")));&#13;
}&#13;
2.9. `ZIP`, `BACKUP`, `RESTORE`&#13;
	•	Nota: Esses comandos não são nativos do PostgreSQL, mas podem ser associados a funcionalidades como pg_dump (backup), pg_restore (restore) ou operações de compressão. Para esta implementação, assumirei que são placeholders para futuras extensões. Se você deseja bloquear ferramentas externas como pg_dump ou pg_restore, isso exigiria integração com hooks de utilitários ou restrições no nível do sistema operacional. Por enquanto, o código suporta essas configurações no .ini, mas não há bloqueio ativo, pois não há funções correspondentes no PostgreSQL.&#13;
2.10. Atualização do Makefile&#13;
	•	Arquivo: src/backend/utils/misc/Makefile&#13;
	•	Linha: Aproximadamente linha 10 (onde OBJS é definido)&#13;
	•	Modificação:&#13;
OBJS = ... restrict_commands.o ...&#13;
&#13;
Exemplo de `restrict_commands.ini`&#13;
[db_financeiro]&#13;
insert=false&#13;
update=false&#13;
delete=false&#13;
drop=false&#13;
truncate=false&#13;
rename=false&#13;
alter_table=false&#13;
alter_column=false&#13;
grant=false&#13;
zip=false&#13;
backup=false&#13;
restore=false&#13;
Msg_block=Comando Bloqueado via Diretivas&#13;
&#13;
[db_erp]&#13;
insert=true&#13;
update=true&#13;
delete=true&#13;
drop=false&#13;
truncate=true&#13;
rename=true&#13;
alter_table=true&#13;
alter_column=true&#13;
grant=true&#13;
zip=true&#13;
backup=true&#13;
restore=true&#13;
Msg_block=Operação não permitida por política&#13;
&#13;
[db_analytics]&#13;
insert=true&#13;
update=false&#13;
delete=false&#13;
drop=true&#13;
truncate=false&#13;
rename=false&#13;
alter_table=false&#13;
alter_column=false&#13;
grant=false&#13;
zip=true&#13;
backup=true&#13;
restore=false&#13;
Msg_block=Acesso restrito por diretivas de segurança&#13;
	•	Localização: Diretório share do PostgreSQL (ex.: /usr/local/pgsql/share/ no Linux/macOS, C:\Program Files\PostgreSQL\15\share no Windows).&#13;
	•	Permissões:&#13;
	◦	Linux/macOS: chmod 640 restrict_commands.ini, chown postgres:postgres restrict_commands.ini&#13;
	◦	Windows: icacls restrict_commands.ini /grant "NETWORK SERVICE:r"&#13;
&#13;
Proposta de Interface no pgAdmin&#13;
A interface no pgAdmin foi atualizada para suportar o novo layout do .ini, incluindo insert, update, zip, backup, restore, e Msg_block.&#13;
Funcionalidade da Interface&#13;
	1	Localização: Aba “Command Restrictions” nas propriedades de cada banco de dados.&#13;
	2	Recursos:&#13;
	◦	Tabela com campos na ordem: insert, update, delete, drop, truncate, rename, alter_table, alter_column, grant, zip, backup, restore.&#13;
	◦	Caixas de seleção para ativar/desativar cada comando.&#13;
	◦	Campo de texto para editar Msg_block.&#13;
	◦	Botão “Read-Only Mode” que define insert=false, update=false, delete=false, truncate=false, alter_table=false, alter_column=false, grant=false.&#13;
	◦	Botão “Save” que valida permissões de escrita, atualiza o .ini, e envia SIGHUP.&#13;
	3	Segurança:&#13;
	◦	Apenas superusuários podem editar.&#13;
	◦	Validação para garantir que o diretório share seja gravável antes de salvar.&#13;
Implementação no pgAdmin&#13;
	1	Backend (Flask):&#13;
from flask import Blueprint, jsonify, request&#13;
import configparser&#13;
import os&#13;
from pgadmin.utils.driver import get_driver&#13;
&#13;
restrict_bp = Blueprint('restrict_commands', __name__)&#13;
&#13;
@restrict_bp.route('/restrict_commands/', methods=['GET', 'POST'])&#13;
def manage_restrictions(did):&#13;
    driver = get_driver()&#13;
    connection = driver.connection(did)&#13;
    config = configparser.ConfigParser()&#13;
    ini_path = os.path.join(connection.share_path(), 'restrict_commands.ini')&#13;
&#13;
    if not os.access(os.path.dirname(ini_path), os.W_OK):&#13;
        return jsonify({'error': 'Cannot write to share directory'}), 403&#13;
&#13;
    if request.method == 'GET':&#13;
        config.read(ini_path)&#13;
        dbname = connection.dbname()&#13;
        restrictions = dict(config[dbname]) if dbname in config else {}&#13;
        return jsonify(restrictions)&#13;
&#13;
    if request.method == 'POST':&#13;
        dbname = connection.dbname()&#13;
        data = request.json&#13;
        config.read(ini_path)&#13;
        if dbname not in config52            config[dbname] = {}&#13;
        for key in ['insert', 'update', 'delete', 'drop', 'truncate', 'rename', &#13;
                    'alter_table', 'alter_column', 'grant', 'zip', 'backup', &#13;
                    'restore', 'Msg_block']:&#13;
            config[dbname][key] = str(data.get(key, True if key != 'Msg_block' else 'Command blocked by policy')).lower()&#13;
        with open(ini_path, 'w') as f:&#13;
            config.write(f)&#13;
        dataframe = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6]})&#13;
        connection.execute_scalar('SELECT pg_reload_conf()')&#13;
        return jsonify({'status': 'success'})&#13;
	2	Frontend (React/Vue):&#13;
	◦	Formulário com caixas de seleção na ordem do .ini.&#13;
	◦	Campo de texto para Msg_block.&#13;
	◦	Botão “Read-Only Mode” para configurações de banco somente leitura.&#13;
	◦	Validação de permissões antes de salvar.&#13;
&#13;
Documentação por Sistema Operacional&#13;
Windows&#13;
Compilação:&#13;
	1	Instale o Visual Studio (Community Edition) com suporte a C++.&#13;
	2	Instale Perl (ex.: Strawberry Perl) e dependências (OpenSSL, zlib).&#13;
	3	Baixe o código-fonte: git clone --branch REL_15_STABLE https://github.com/postgres/postgres.git&#13;
	4	&#13;
	5	Adicione/modifique os arquivos listados.&#13;
	6	Compile: perl win32_mak.pl&#13;
	7	nmake /f win32.mak&#13;
	8	&#13;
	9	Copie os binários para C:\Program Files\PostgreSQL\15.&#13;
Configuração:&#13;
	•	Coloque restrict_commands.ini em C:\Program Files\PostgreSQL\15\share.&#13;
	•	Configure permissões: icacls "C:\Program Files\PostgreSQL\15\share\restrict_commands.ini" /grant "NETWORK SERVICE:r"&#13;
	•	&#13;
	•	Reinicie o servidor: pg_ctl restart -D "C:\Program Files\PostgreSQL\15\data"&#13;
	•	&#13;
Logs:&#13;
	•	Bloqueados: C:\Program Files\PostgreSQL\15\data\_blocked.log&#13;
	•	Permitidos: C:\Program Files\PostgreSQL\15\data\_allowed.log&#13;
Linux&#13;
Compilação:&#13;
	1	Instale dependências: sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev&#13;
	2	&#13;
	3	Baixe o código-fonte: git clone --branch REL_15_STABLE https://github.com/postgres/postgres.git&#13;
	4	cd postgres&#13;
	5	&#13;
	6	Adicione/modifique os arquivos.&#13;
	7	Compile: ./configure --prefix=/usr/local/pgsql&#13;
	8	make&#13;
	9	sudo make install&#13;
	10	&#13;
Configuração:&#13;
	•	Coloque restrict_commands.ini em /usr/local/pgsql/share/.&#13;
	•	Ajuste permissões: sudo chown postgres:postgres /usr/local/pgsql/share/restrict_commands.ini&#13;
	•	sudo chmod 640 /usr/local/pgsql/share/restrict_commands.ini&#13;
	•	&#13;
	•	Inicie o servidor: /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data&#13;
	•	/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start&#13;
	•	&#13;
Logs:&#13;
	•	Bloqueados: /usr/local/pgsql/data/_blocked.log&#13;
	•	Permitidos: /usr/local/pgsql/data/_allowed.log&#13;
macOS&#13;
Compilação:&#13;
	1	Instale o Xcode: xcode-select --install&#13;
	2	&#13;
	3	Instale dependências via Homebrew: brew install openssl readline libxml2 libxslt&#13;
	4	&#13;
	5	Baixe e compile: git clone --branch REL_15_STABLE https://github.com/postgres/postgres.git&#13;
	6	cd postgres&#13;
	7	./configure --prefix=/usr/local/pgsql --with-openssl&#13;
	8	make&#13;
	9	sudo make install&#13;
	10	&#13;
Configuração:&#13;
	•	Coloque restrict_commands.ini em /usr/local/pgsql/share/.&#13;
	•	Ajuste permissões: sudo chown postgres:postgres /usr/local/pgsql/share/restrict_commands.ini&#13;
	•	sudo chmod 640 /usr/local/pgsql/share/restrict_commands.ini&#13;
	•	&#13;
	•	Inicie o servidor: /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data&#13;
	•	/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start&#13;
	•	&#13;
Logs:&#13;
	•	Bloqueados: /usr/local/pgsql/data/_blocked.log&#13;
	•	Permitidos: /usr/local/pgsql/data/_allowed.log&#13;
&#13;
Arquivos Modificados&#13;
	1	src/backend/utils/misc/restrict_commands.c:&#13;
	◦	Novo arquivo com a lógica de bloqueio e logging.&#13;
	2	src/include/utils/restrict_commands.h:&#13;
	◦	Novo arquivo com o cabeçalho.&#13;
	3	src/backend/commands/dbcommands.c:&#13;
	◦	Função: dropdb, linha ~200, bloqueio de DROP DATABASE.&#13;
	4	src/backend/commands/tablecmds.c:&#13;
	◦	Função: RemoveRelations, linha ~400, bloqueio de DROP TABLE.&#13;
	◦	Função: ExecuteTruncateGuts, linha ~6000, bloqueio de TRUNCATE.&#13;
	◦	Função: RenameRelation, linha ~3000, bloqueio de RENAME.&#13;
	◦	Função: AlterTable, linha ~1000, bloqueio de ALTER TABLE e ALTER COLUMN.&#13;
	5	src/backend/executor/nodeModifyTable.c:&#13;
	◦	Função: ExecInsert, linha ~800, bloqueio de INSERT.&#13;
	◦	Função: ExecUpdate, linha ~1200, bloqueio de UPDATE.&#13;
	◦	Função: ExecDelete, linha ~1000, bloqueio de DELETE.&#13;
	6	src/backend/nodes/aclchk.c:&#13;
	◦	Função: ExecGrantStmt, linha ~200, bloqueio de GRANT.&#13;
	7	src/backend/utils/misc/Makefile:&#13;
	◦	Linha ~10, adicionar restrict_commands.o.&#13;
&#13;
Revisão de Funcionalidade&#13;
	•	Layout do .ini: Segue a ordem especificada, com Msg_block para mensagens personalizadas.&#13;
	•	Comportamento do .ini: Sem .ini, todos os comandos são permitidos. Com .ini, bloqueia comandos com false, usando Msg_block.&#13;
	•	Ordem das Verificações: Verifica na ordem do .ini.&#13;
	•	Logs:&#13;
	◦	Bloqueados: Registrados em _blocked.log para comandos com false.&#13;
	◦	Permitidos: Registrados em _allowed.log para comandos com true.&#13;
	•	Validação de Escrita: Verifica permissões de escrita para logs e .ini (via pgAdmin).&#13;
	•	Portabilidade: Funciona em Windows, Linux e macOS com funções portáteis.&#13;
	•	Interface pgAdmin: Suporta todos os comandos, incluindo zip, backup, restore, e modo somente leitura.&#13;
&#13;
Observações Finais&#13;
	•	Testes: Teste em um ambiente de desenvolvimento, especialmente os comandos zip, backup, restore (se forem implementados) e a interface no pgAdmin.&#13;
	•	Segurança: Restrinja permissões no .ini e valide alterações no pgAdmin.&#13;
	•	Comunidade: Envie para pgsql-hackers com uma explicação dos benefícios, destacando a proteção contra roubo de dados.&#13;
	•	Comandos zip, backup, restore: Atualmente são placeholders. Se precisar de bloqueio para pg_dump ou pg_restore, sugiro hooks adicionais ou restrições no sistema operacional.&#13;
Se precisar de scripts adicionais, mais detalhes sobre a integração com pgAdmin, ou testes específicos, por favor, avise! A solução está pronta para evitar roubo de dados, com flexibilidade para bancos somente leitura e auditoria robusta.&#13;
&#13;
&#13;
Abaixo, apresento um passo a passo detalhado para compilar e atualizar o PostgreSQL com as mudanças propostas, incluindo o bloqueio de comandos (INSERT, UPDATE, DELETE, DROP, TRUNCATE, RENAME, ALTER TABLE, ALTER COLUMN, GRANT, ZIP, BACKUP, RESTORE) com base no arquivo restrict_commands.ini. O guia cobre Windows, Linux e macOS, detalhando a criação dos arquivos, aplicação das modificações, compilação, configuração e validação. A solução incorpora todas as melhorias solicitadas, como validação do .ini, armazenamento em memória, recarga via SIGHUP, logs para comandos permitidos e bloqueados, e suporte a mensagens personalizadas (Msg_block).&#13;
&#13;
Passo a Passo para Compilar e Atualizar o PostgreSQL&#13;
Pré-requisitos Gerais (Todos os Sistemas Operacionais)&#13;
	1	Baixe o Código-Fonte do PostgreSQL:&#13;
	◦	Use a versão 15 (estável no momento da solicitação): git clone --branch REL_15_STABLE https://github.com/postgres/postgres.git&#13;
	◦	cd postgres&#13;
	◦	&#13;
	◦	Alternativamente, baixe o tarball de https://www.postgresql.org/ftp/source/v15.8/ e extraia.&#13;
	2	Ferramentas Necessárias:&#13;
	◦	Um compilador C (GCC no Linux/macOS, Visual Studio no Windows).&#13;
	◦	Perl (para scripts de configuração).&#13;
	◦	Dependências: libreadline, zlib, openssl, libxml2, libxslt, flex, bison.&#13;
	3	Ambiente de Desenvolvimento:&#13;
	◦	Configure um ambiente de teste para evitar interferências em instalações de produção.&#13;
	◦	Faça backup de qualquer instalação existente do PostgreSQL.&#13;
&#13;
1. Criar e Modificar Arquivos&#13;
1.1. Criar `restrict_commands.c`&#13;
	•	Caminho: src/backend/utils/misc/restrict_commands.c&#13;
	•	Conteúdo: Copie o código abaixo:&#13;
#include "postgres.h"&#13;
#include "utils/guc.h"&#13;
#include "storage/fd.h"&#13;
#include "utils/elog.h"&#13;
#include "utils/memutils.h"&#13;
#include &#13;
#include &#13;
#include &#13;
#include &#13;
&#13;
#define MAX_DBNAME 64&#13;
#define MAX_OPERATIONS 12&#13;
#define MAX_MSG 256&#13;
&#13;
typedef struct {&#13;
    char dbname[MAX_DBNAME];&#13;
    bool insert_allowed;&#13;
    bool update_allowed;&#13;
    bool delete_allowed;&#13;
    bool drop_allowed;&#13;
    bool truncate_allowed;&#13;
    bool rename_allowed;&#13;
    bool alter_table_allowed;&#13;
    bool alter_column_allowed;&#13;
    bool grant_allowed;&#13;
    bool zip_allowed;&#13;
    bool backup_allowed;&#13;
    bool restore_allowed;&#13;
    char msg_block[MAX_MSG];&#13;
} DbConfig;&#13;
&#13;
static char INI_PATH[1024];&#13;
static char LOG_PATH_BASE[1024];&#13;
static DbConfig *configs = NULL;&#13;
static int num_configs = 0;&#13;
static MemoryContext RestrictCommandsContext = NULL;&#13;
static bool ini_valid = false;&#13;
&#13;
static char *trim(char *str) {&#13;
    while (isspace(*str)) str++;&#13;
    char *end = str + strlen(str) - 1;&#13;
    while (end &gt; str &amp;&amp; isspace(*end)) *end-- = '\0';&#13;
    return str;&#13;
}&#13;
&#13;
static bool load_ini_file(void) {&#13;
    FILE *fp = AllocateFile(INI_PATH, "r");&#13;
    if (!fp) {&#13;
        ereport(WARNING, (errmsg("Cannot open INI file: %s", INI_PATH)));&#13;
        ini_valid = false;&#13;
        return false;&#13;
    }&#13;
&#13;
    if (configs) {&#13;
        pfree(configs);&#13;
        configs = NULL;&#13;
        num_configs = 0;&#13;
    }&#13;
&#13;
    MemoryContext oldcontext = MemoryContextSwitchTo(RestrictCommandsContext);&#13;
    List *temp_configs = NIL;&#13;
    char line[256];&#13;
    char current_dbname[MAX_DBNAME] = "";&#13;
    DbConfig *config = NULL;&#13;
&#13;
    while (fgets(line, sizeof(line), fp)) {&#13;
        line[strcspn(line, "\r\n")] =  cib0;&#13;
        if (line[0] == '\0' || line[0] == ';') continue;&#13;
&#13;
        if (line[0] == '[' &amp;&amp; line[strlen(line)-1] == ']') {&#13;
            if (config) {&#13;
                if (config-&gt;msg_block[0] == '\0') {&#13;
                    strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
                }&#13;
                temp_configs = lappend(temp_configs, config);&#13;
            }&#13;
            config = palloc0(sizeof(DbConfig));&#13;
            strncpy(config-&gt;dbname, line + 1, strlen(line) - 2);&#13;
            config-&gt;dbname[strlen(line) - 2] = '\0';&#13;
            config-&gt;insert_allowed = true;&#13;
            config-&gt;update_allowed = true;&#13;
            config-&gt;delete_allowed = true;&#13;
            config-&gt;drop_allowed = true;&#13;
            config-&gt;truncate_allowed = true;&#13;
            config-&gt;rename_allowed = true;&#13;
            config-&gt;alter_table_allowed = true;&#13;
            config-&gt;alter_column_allowed = true;&#13;
            config-&gt;grant_allowed = true;&#13;
            config-&gt;zip_allowed = true;&#13;
            config-&gt;backup_allowed = true;&#13;
            config-&gt;restore_allowed = true;&#13;
            strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
            continue;&#13;
        }&#13;
&#13;
        if (config) {&#13;
            char *key = strtok(line, "=");&#13;
            char *value = strtok(NULL, "=");&#13;
            if (!key || !value) continue;&#13;
&#13;
            key = trim(key);&#13;
            value = trim(value);&#13;
&#13;
            if (strcmp(key, "insert") == 0) config-&gt;insert_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "update") == 0) config-&gt;update_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "delete") == 0) config-&gt;delete_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "drop") == 0) config-&gt;drop_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "truncate") == 0) config-&gt;truncate_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "rename") == 0) config-&gt;rename_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "alter_table") == 0) config-&gt;alter_table_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "alter_column") == 0) config-&gt;alter_column_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "grant") == 0) config-&gt;grant_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "zip") == 0) config-&gt;zip_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "backup") == 0) config-&gt;backup_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "restore") == 0) config-&gt;restore_allowed = strcmp(value, "true") == 0;&#13;
            else if (strcmp(key, "Msg_block") == 0) strncpy(config-&gt;msg_block, value, MAX_MSG - 1);&#13;
        }&#13;
    }&#13;
&#13;
    if (config) {&#13;
        if (config-&gt;msg_block[0] == '\0') {&#13;
            strcpy(config-&gt;msg_block, "Command blocked by policy");&#13;
        }&#13;
        temp_configs = lappend(temp_configs, config);&#13;
    }&#13;
&#13;
    FreeFile(fp);&#13;
&#13;
    num_configs = list_length(temp_configs);&#13;
    if (num_configs &gt; 0) {&#13;
        configs = palloc(num_configs * sizeof(DbConfig));&#13;
        ListCell *lc;&#13;
        int i = 0;&#13;
        foreach(lc, temp_configs) {&#13;
            configs[i++] = *(DbConfig *)lfirst(lc);&#13;
        }&#13;
        list_free_deep(temp_configs);&#13;
    }&#13;
&#13;
    MemoryContextSwitchTo(oldcontext);&#13;
    ini_valid = true;&#13;
    return true;&#13;
}&#13;
&#13;
void _PG_init(void) {&#13;
    char share_path[MAXPGPATH];&#13;
    get_share_path(my_exec_path, share_path);&#13;
    snprintf(INI_PATH, sizeof(INI_PATH), "%s/restrict_commands.ini", share_path);&#13;
    snprintf(LOG_PATH_BASE, sizeof(LOG_PATH_BASE), "%s/", DataDir);&#13;
&#13;
    RestrictCommandsContext = AllocSetContextCreate(TopMemoryContext,&#13;
                                                   "RestrictCommands",&#13;
                                                   ALLOCSET_DEFAULT_SIZES);&#13;
&#13;
    load_ini_file();&#13;
&#13;
    BackgroundWorkerInitialize();&#13;
    RegisterBackgroundWorkerSighup(load_ini_file);&#13;
}&#13;
&#13;
bool check_command_permission(const char *dbname, const char *operation, char **msg_block) {&#13;
    if (!ini_valid) {&#13;
        return true;&#13;
    }&#13;
&#13;
    for (int i = 0; i &lt; num_configs; i++) {&#13;
        if (strcmp(configs[i].dbname, dbname) == 0) {&#13;
            *msg_block = configs[i].msg_block;&#13;
            if (strcmp(operation, "insert") == 0) {&#13;
                if (configs[i].insert_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].insert_allowed;&#13;
            }&#13;
            if (strcmp(operation, "update") == 0) {&#13;
                if (configs[i].update_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].update_allowed;&#13;
            }&#13;
            if (strcmp(operation, "delete") == 0) {&#13;
                if (configs[i].delete_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].delete_allowed;&#13;
            }&#13;
            if (strcmp(operation, "drop") == 0) {&#13;
                if (configs[i].drop_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].drop_allowed;&#13;
            }&#13;
            if (strcmp(operation, "truncate") == 0) {&#13;
                if (configs[i].truncate_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].truncate_allowed;&#13;
            }&#13;
            if (strcmp(operation, "rename") == 0) {&#13;
                if (configs[i].rename_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].rename_allowed;&#13;
            }&#13;
            if (strcmp(operation, "alter_table") == 0) {&#13;
                if (configs[i].alter_table_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].alter_table_allowed;&#13;
            }&#13;
            if (strcmp(operation, "alter_column") == 0) {&#13;
                if (configs[i].alter_column_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].alter_column_allowed;&#13;
            }&#13;
            if (strcmp(operation, "grant") == 0) {&#13;
                if (configs[i].grant_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].grant_allowed;&#13;
            }&#13;
            if (strcmp(operation, "zip") == 0) {&#13;
                if (configs[i].zip_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].zip_allowed;&#13;
            }&#13;
            if (strcmp(operation, "backup") == 0) {&#13;
                if (configs[i].backup_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].backup_allowed;&#13;
            }&#13;
            if (strcmp(operation, "restore") == 0) {&#13;
                if (configs[i].restore_allowed) log_allowed_command(dbname, operation);&#13;
                return configs[i].restore_allowed;&#13;
            }&#13;
            return true;&#13;
        }&#13;
    }&#13;
&#13;
    return true;&#13;
}&#13;
&#13;
void log_blocked_command(const char *dbname, const char *operation) {&#13;
    char logpath[512];&#13;
    snprintf(logpath, sizeof(logpath), "%s%s_blocked.log", LOG_PATH_BASE, dbname);&#13;
&#13;
    if (access(LOG_PATH_BASE, W_OK) != 0) {&#13;
        ereport(WARNING, (errmsg("Cannot write to log directory: %s", LOG_PATH_BASE)));&#13;
        return;&#13;
    }&#13;
&#13;
    FILE *logf = AllocateFile(logpath, "a");&#13;
    if (!logf) {&#13;
        ereport(WARNING, (errmsg("Cannot open log file: %s", logpath)));&#13;
        return;&#13;
    }&#13;
&#13;
    time_t now = time(NULL);&#13;
    char *timestamp = ctime(&amp;now);&#13;
    timestamp[strlen(timestamp)-1] = '\0';&#13;
&#13;
    char *user = GetUserNameOrNull();&#13;
    if (!user) user = "unknown";&#13;
&#13;
    fprintf(logf, "[%s] USER: %s tried: %s (BLOCKED)\n", timestamp, user, operation);&#13;
    FreeFile(logf);&#13;
}&#13;
&#13;
void log_allowed_command(const char *dbname, const char *operation) {&#13;
    char logpath[512];&#13;
    snprintf(logpath, sizeof(logpath), "%s%s_allowed.log", LOG_PATH_BASE, dbname);&#13;
&#13;
    if (access(LOG_PATH_BASE, W_OK) != 0) {&#13;
        ereport(WARNING, (errmsg("Cannot write to log directory: %s", LOG_PATH_BASE)));&#13;
        return;&#13;
    }&#13;
&#13;
    FILE *logf = AllocateFile(logpath, "a");&#13;
    if (!logf) {&#13;
        ereport(WARNING, (errmsg("Cannot open log file: %s", logpath)));&#13;
        return;&#13;
    }&#13;
&#13;
    time_t now = time(NULL);&#13;
    char *timestamp = ctime(&amp;now);&#13;
    timestamp[strlen(timestamp)-1] = '\0';&#13;
&#13;
    char *user = GetUserNameOrNull();&#13;
    if (!user) user = "unknown";&#13;
&#13;
    fprintf(logf, "[%s] USER: %s executed: %s (ALLOWED)\n", timestamp, user, operation);&#13;
    FreeFile(logf);&#13;
}&#13;
1.2. Criar `restrict_commands.h`&#13;
	•	Caminho: src/include/utils/restrict_commands.h&#13;
	•	Conteúdo:&#13;
#ifndef RESTRICT_COMMANDS_H&#13;
#define RESTRICT_COMMANDS_H&#13;
&#13;
bool check_command_permission(const char *dbname, const char *operation, char **msg_block);&#13;
void log_blocked_command(const char *dbname, const char *operation);&#13;
void log_allowed_command(const char *dbname, const char *operation);&#13;
&#13;
#endif /* RESTRICT_COMMANDS_H */&#13;
1.3. Modificar Arquivos Existentes&#13;
Aplique as modificações abaixo, adicionando verificações para cada comando. As linhas são aproximadas para o PostgreSQL 15.8.&#13;
	1	src/backend/commands/dbcommands.c:&#13;
	◦	Função: dropdb (linha ~200)&#13;
	◦	Modificação: #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(dbname, "drop", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DROP DATABASE is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	2	src/backend/commands/tablecmds.c:&#13;
	◦	Função: RemoveRelations (linha ~400, para DROP TABLE): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "drop", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DROP is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	◦	Função: ExecuteTruncateGuts (linha ~6000): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "truncate", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "TRUNCATE is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	◦	Função: RenameRelation (linha ~3000): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "rename", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "RENAME is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	◦	Função: AlterTable (linha ~1000): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "alter_table", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "ALTER TABLE is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	if (cmd-&gt;subtype == AT_AlterColumnType &amp;&amp;&#13;
	◦	    !check_command_permission(get_database_name(MyDatabaseId), "alter_column", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "ALTER COLUMN is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	3	src/backend/executor/nodeModifyTable.c:&#13;
	◦	Função: ExecInsert (linha ~800): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "insert", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "INSERT is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	◦	Função: ExecUpdate (linha ~1200): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "update", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "UPDATE is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	◦	Função: ExecDelete (linha ~1000): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "delete", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "DELETE is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	4	src/backend/nodes/aclchk.c:&#13;
	◦	Função: ExecGrantStmt (linha ~200): #include "utils/restrict_commands.h"&#13;
	◦	char *msg_block = NULL;&#13;
	◦	if (!check_command_permission(get_database_name(MyDatabaseId), "grant", &amp;msg_block)) {&#13;
	◦	    ereport(ERROR, (errmsg("%s", msg_block ? msg_block : "GRANT is blocked for this database by policy")));&#13;
	◦	}&#13;
	◦	&#13;
	5	src/backend/utils/misc/Makefile:&#13;
	◦	Linha: ~10 (onde OBJS é definido)&#13;
	◦	Modificação: OBJS = ... restrict_commands.o ...&#13;
	◦	&#13;
Nota sobre ZIP, BACKUP, RESTORE:&#13;
	•	Esses comandos não têm funções correspondentes no PostgreSQL. Para bloquear pg_dump (backup) ou pg_restore (restore), seria necessário integrar com hooks de utilitários ou restrições no sistema operacional. Atualmente, o código suporta essas configurações no .ini, mas não aplica bloqueios.&#13;
&#13;
2. Compilação por Sistema Operacional&#13;
Windows&#13;
	1	Instalar Dependências:&#13;
	◦	Visual Studio (Community Edition) com suporte a C++.&#13;
	◦	Perl (ex.: Strawberry Perl).&#13;
	◦	Dependências: OpenSSL, zlib. Instale via vcpkg ou baixe pré-compiladas.&#13;
	2	Configurar o Ambiente:&#13;
	◦	Abra o Visual Studio Developer Command Prompt.&#13;
	◦	Navegue até o diretório do código-fonte: cd C:\path\to\postgres&#13;
	◦	&#13;
	3	_&#13;
Compilar:&#13;
perl win32_mak.pl&#13;
nmake /f win32.mak&#13;
	4	Instalar:&#13;
	◦	Copie os binários gerados (em Release) para C:\Program Files\PostgreSQL\15.&#13;
	5	Verificar:&#13;
	◦	Confirme que restrict_commands.o está incluído nos objetos compilados (verifique src/backend/utils/misc/).&#13;
Linux&#13;
	1	Instalar Dependências: sudo apt-get install build-essential libreadline-dev zlib1g-dev flex bison libxml2-dev libxslt-dev libssl-dev&#13;
	2	&#13;
	3	Configurar o Ambiente:&#13;
	◦	Navegue até o diretório do código-fonte: cd /path/to/postgres&#13;
	◦	&#13;
	4	Compilar: ./configure --prefix=/usr/local/pgsql&#13;
	5	make&#13;
	6	sudo make install&#13;
	7	&#13;
	8	Verificar:&#13;
	◦	Confirme que restrict_commands.o está no diretório src/backend/utils/misc/.libs/ após o make.&#13;
macOS&#13;
	1	Instalar Dependências:&#13;
	◦	Instale o Xcode: xcode-select --install&#13;
	◦	&#13;
	◦	Instale via Homebrew: brew install openssl readline libxml2 libxslt&#13;
	◦	&#13;
	2	Configurar o Ambiente:&#13;
	◦	Navegue até o diretório do código-fonte: cd /path/to/postgres&#13;
	◦	&#13;
	3	Compilar: ./configure --prefix=/usr/local/pgsql --with-openssl&#13;
	4	make&#13;
	5	sudo make install&#13;
	6	&#13;
	7	Verificar:&#13;
	◦	Confirme que restrict_commands.o está no diretório src/backend/utils/misc/ após o make.&#13;
&#13;
3. Configurar o Arquivo `restrict_commands.ini`&#13;
	1	Criar o Arquivo:&#13;
	◦	Caminho: Diretório share do PostgreSQL (ex.: /usr/local/pgsql/share/ no Linux/macOS, C:\Program Files\PostgreSQL\15\share no Windows).&#13;
	◦	Conteúdo:&#13;
[db_financeiro]&#13;
insert=false&#13;
update=false&#13;
delete=false&#13;
drop=false&#13;
truncate=false&#13;
rename=false&#13;
alter_table=false&#13;
alter_column=false&#13;
grant=false&#13;
zip=false&#13;
backup=false&#13;
restore=false&#13;
Msg_block=Comando Bloqueado via Diretivas&#13;
&#13;
[db_erp]&#13;
insert=true&#13;
update=true&#13;
delete=true&#13;
drop=false&#13;
truncate=true&#13;
rename=true&#13;
alter_table=true&#13;
alter_column=true&#13;
grant=true&#13;
zip=true&#13;
backup=true&#13;
restore=true&#13;
Msg_block=Operação não permitida por política&#13;
&#13;
[db_analytics]&#13;
insert=true&#13;
update=false&#13;
delete=false&#13;
drop=true&#13;
truncate=false&#13;
rename=false&#13;
alter_table=false&#13;
alter_column=false&#13;
grant=false&#13;
zip=true&#13;
backup=true&#13;
restore=false&#13;
Msg_block=Acesso restrito por diretivas de segurança&#13;
	2	Configurar Permissões:&#13;
	◦	Linux/macOS: sudo chown postgres:postgres /usr/local/pgsql/share/restrict_commands.ini&#13;
	◦	sudo chmod 640 /usr/local/pgsql/share/restrict_commands.ini&#13;
	◦	&#13;
	◦	Windows: icacls "C:\Program Files\PostgreSQL\15\share\restrict_commands.ini" /grant "NETWORK SERVICE:r"&#13;
	◦	&#13;
&#13;
4. Iniciar e Configurar o Servidor&#13;
Windows&#13;
	1	Inicializar o Banco de Dados (se necessário): "C:\Program Files\PostgreSQL\15\bin\initdb" -D "C:\Program Files\PostgreSQL\15\data"&#13;
	2	&#13;
	3	Iniciar o Servidor: "C:\Program Files\PostgreSQL\15\bin\pg_ctl" -D "C:\Program Files\PostgreSQL\15\data" -l logfile start&#13;
	4	&#13;
	5	Criar Bancos de Teste: "C:\Program Files\PostgreSQL\15\bin\createdb" db_financeiro&#13;
	6	"C:\Program Files\PostgreSQL\15\bin\createdb" db_erp&#13;
	7	"C:\Program Files\PostgreSQL\15\bin\createdb" db_analytics&#13;
	8	&#13;
Linux&#13;
	1	Inicializar o Banco de Dados: /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data&#13;
	2	&#13;
	3	Iniciar o Servidor: /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start&#13;
	4	&#13;
	5	Criar Bancos de Teste: /usr/local/pgsql/bin/createdb db_financeiro&#13;
	6	/usr/local/pgsql/bin/createdb db_erp&#13;
	7	/usr/local/pgsql/bin/createdb db_analytics&#13;
	8	&#13;
macOS&#13;
	1	Inicializar o Banco de Dados: /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data&#13;
	2	&#13;
	3	Iniciar o Servidor: /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start&#13;
	4	&#13;
	5	Criar Bancos de Teste: /usr/local/pgsql/bin/createdb db_financeiro&#13;
	6	/usr/local/pgsql/bin/createdb db_erp&#13;
	7	/usr/local/pgsql/bin/createdb db_analytics&#13;
	8	&#13;
&#13;
5. Testar as Mudanças&#13;
	1	Conectar ao Banco:&#13;
	◦	Use psql: psql -U postgres -d db_financeiro&#13;
	◦	&#13;
	2	Testar Comandos Bloqueados:&#13;
	◦	No banco db_financeiro, tente: INSERT INTO test_table VALUES (1);&#13;
	◦	&#13;
	▪	Esperado: Erro com mensagem “Comando Bloqueado via Diretivas”.&#13;
	▪	Verifique o log em /db_financeiro_blocked.log.&#13;
	3	Testar Comandos Permitidos:&#13;
	◦	No banco db_erp, tente: INSERT INTO test_table VALUES (1);&#13;
	◦	&#13;
	▪	Esperado: Sucesso, com registro em /db_erp_allowed.log.&#13;
	4	Testar Sem .ini:&#13;
	◦	Remova ou renomeie restrict_commands.ini e reinicie o servidor.&#13;
	◦	Tente comandos bloqueados (ex.: DROP DATABASE db_financeiro).&#13;
	◦	equEsperado: Todos os comandos são permitidos.&#13;
	5	Testar Recarga:&#13;
	◦	Edite restrict_commands.ini (ex.: mude insert=true para false em db_erp).&#13;
	◦	Envie SIGHUP: pg_ctl reload -D /path/to/data&#13;
	◦	&#13;
	◦	Teste novamente para confirmar as novas configurações.&#13;
&#13;
6. Configurar a Interface no pgAdmin (Opcional)&#13;
	1	Modificar o pgAdmin:&#13;
	◦	Adicione a rota Flask ao backend (conforme descrito anteriormente).&#13;
	◦	Atualize o frontend para incluir todos os campos do .ini.&#13;
	2	Testar a Interface:&#13;
	◦	Acesse a aba “Command Restrictions” no pgAdmin para um banco de dados.&#13;
	◦	Altere configurações e salve.&#13;
	◦	Verifique se o .ini foi atualizado e as configurações recarregadas.&#13;
&#13;
7. Validações Finais&#13;
	•	Logs:&#13;
	◦	Confirme que _blocked.log registra tentativas bloqueadas.&#13;
	◦	Confirme que _allowed.log registra comandos permitidos.&#13;
	•	Permissões:&#13;
	◦	Verifique se o .ini e os logs têm permissões corretas.&#13;
	•	Portabilidade:&#13;
	◦	Teste em cada sistema operacional para garantir consistência.&#13;
	•	Mensagem Personalizada:&#13;
	◦	Confirme que Msg_block aparece nos erros.&#13;
&#13;
Observações&#13;
	•	Comandos ZIP, BACKUP, RESTORE:&#13;
	◦	Não implementados no PostgreSQL. Se necessário, integre com hooks de utilitários ou configure restrições no sistema operacional.&#13;
	•	Erros Potenciais:&#13;
	◦	Falha na Compilação: Verifique dependências e a inclusão de restrict_commands.o no Makefile.&#13;
	◦	Permissões: Certifique-se de que o usuário do PostgreSQL tem acesso ao diretório share e data.&#13;
	•	Testes: Execute testes exaustivos em um ambiente de desenvolvimento antes de implantar em produção.&#13;
	•	Comunidade: Para propor à comunidade PostgreSQL, envie para pgsql-hackers com uma descrição clara dos benefícios, como proteção contra roubo de dados.&#13;
Se precisar de scripts adicionais (ex.: para automatizar a criação de arquivos), mais detalhes sobre a integração com pgAdmin, ou assistência com testes, por favor, avise!&#13;
&#13;
--&#13;
Adriano José Boller&#13;
______________________________________________&#13;
Consultor e Representante Oficial da&#13;
PcSoft no Brasil&#13;
+55 (41) 99949 1800&#13;
adrianoboller@gmail.com&#13;
skype: adrianoboller&#13;
http://wxinformatica.com.br/</description><ttl>30</ttl><generator>WEBDEV</generator><language>pt_BR</language><link>https://forum.pcsoft.fr/en-US/pcsoft.br.windev/4591-projeto-postgresql-bloqueio-comandos-proibidos-alterando-fonte/read.awp</link><title>Projeto PostgreSQL bloqueio de comandos proibidos alterando o fonte em c</title><managingEditor>moderateur@pcsoft.fr (The moderator)</managingEditor><webMaster>webmaster@pcsoft.fr (The webmaster)</webMaster><item><author>Boller</author><category>pcsoft.br.windev</category><comments>https://forum.pcsoft.fr/en-US/pcsoft.br.windev/4591-projeto-postgresql-bloqueio-comandos-proibidos-alterando-fonte-4592/read.awp</comments><pubDate>5 May 2025 18:01:15 Z</pubDate><description>Em inglês &#13;
&#13;
Below is a comprehensive and polished English version of the documentation for the PostgreSQL command restriction …</description><guid isPermaLink="true">https://forum.pcsoft.fr/en-US/pcsoft.br.windev/4591-projeto-postgresql-bloqueio-comandos-proibidos-alterando-fonte-4592/read.awp</guid><link>https://forum.pcsoft.fr/en-US/pcsoft.br.windev/4591-projeto-postgresql-bloqueio-comandos-proibidos-alterando-fonte-4592/read.awp</link><source url="https://forum.pcsoft.fr/en-US/pcsoft.br.windev/4591-projeto-postgresql-bloqueio-comandos-proibidos-alterando-fonte/read.awp">Projeto PostgreSQL bloqueio de comandos proibidos alterando o fonte em c</source><title>Re: Projeto PostgreSQL bloqueio de comandos proibidos alterando o fonte em c</title></item></channel></rss>
