Nível SC: Mediano
Nível PHP: Mediano - Avançado se for implementar.
Nível SQL: Avançado
Nível do Tutorial: Avançado
DB Utilizado: MySQL
- Se você começou no SC ontem e já quer fazer isso, estude antes.
Olá pessoal do fórum, eu estou a desenvolver um sistema e me surgiu uma necessidade de criar um módulo de segurança com a seguinte estrutura:
Revenda - Empresa que vai acessar o sistema
Unidade - Unidade/Filial desta empresa que vai acessar.
Módulo - Módulo de aplicações.
Usuário - Usuário que logará.
O scriptcase te da a opção de criar permissões por grupos de usuários, bom legal mas não ajudou muito no que eu queria, então eu resolvi desenvolver meu próprio módulo de segurança usando as funções do SC, e vou passar para vocês como foi feito:
Bom primeiro vou explicar, “módulo de aplicações” nada mais é que um conjunto de aplicações que se relacionam no sistema.
Ex.: Eu tenho o cadastro de Regiões, que também tem as cidades e o cadastro é feito por meio de um mestre-detalhe e visualizados juntos em uma consulta com sub-consulta, então eu criei o módulo Cadastro de Regiões e no cadastro coloquei ele ligado as respectivas aplicações que fazem parte do cadastro. Então quando eu vou pra minha tela de permissões eu não tenho aquele monte de aplicação eu tenho o módulo de Regiões, e setando permissão pro módulo, automaticamente todas as aplicações estarão permissivas.
Partindo disso vamos ao início:
1- Criando o módulo de segurança.
- Neste que fiz eu usei o módulo básico do SC, somente por usuário, para criar tela de login, menu, etc…
- Após criado você precisará das seguintes tabelas: (usarei as minhas de exemplo).
Aplicações:
Nome: cadapps
NomApp - Nome - PK
DesApp - Descrição
TipApp - Tipo
Módulos:
Nome: cadmod
CodMod - Código - PK
NomMod - Nome
SitMod - Situação (A-Ativo I-Inativo)
Essa tabela é para a criação do N-N Relations (Cortesia do Saulo Borges que me ensinou a usar)
Módulos/Aplicação:
Nome: modapps
NomApp - Nome Aplicação - PK FK
CodMod - Código Módulo - PK FK
Tabela de permissão, nesta tabela eu estou usando a minha estrutura de segurança mas vocês poderão criar a de vocês. No meu caso é (Revenda - Unidade - Módulo - Usuário).
Módulo/Usuário
Nome: modusu
CodRev - Código da Revenda - PK FK
CodUni - Código da Unidade - PK FK
CodMod - Código do Módulo - PK FK
CodUsu - Notem que não estou usando login, pois eu adicionei uma chave primária AI na tabela de usuários criada pelo SC. - PK FK
- Obs.: Isso é uma chave composta de 4 primary keys onde 1 chave corresponde a uma sequencia de código que não se repete, (1,1,1,1) (1,2,1,1) (1,1,2,1) […]
AceMod - Permissão de Acesso - VARCHAR 1 (Y-SIM) (N-NÃO)
IncMod - Permissão de Inclusão - VARCHAR 1 (Y-SIM) (N-NÃO)
ExcMod - Permissão de Exclusão - VARCHAR 1 (Y-SIM) (N-NÃO)
AltMod - Permissão de Alteração - VARCHAR 1 (Y-SIM) (N-NÃO)
ImpMod - Permissão de Impressão - VARCHAR 1 (Y-SIM) (N-NÃO)
ExpMod - Permissão de Exportação - VARCHAR 1 (Y-SIM) (N-NÃO)
- Agora precisamos criar uma aplicaçãozinha muito importante que não foi criada automática pelo SC pois ela pertence ao módulo por aplicações e nós fizemos por usuário, então vamos criar a:
sync_apps - Sincronia de Aplicações
onValidade:
$dir = opendir($this->Ini->path_aplicacao . "..");
$arr_apps = array();
while ($file = readdir($dir))
{
if ($file != '.' && $file != '..' && $file != '_lib')
{
$arr_apps[] = $file;
}
}
closedir($dir);
sc_select(rs, "SELECT NomApp FROM cadapps");
$arr_apps_db = array();
while(!$rs->EOF)
{
$arr_apps_db[] = $rs->fields[0];
$rs->MoveNext();
}
$rs->Close();
$arr_apps = array_diff($arr_apps, $arr_apps_db);
foreach($arr_apps as $k => $app)
{
$file_ini = $this->Ini->path_aplicacao. "../".$app . "/".$app ."_ini.txt";
if(is_file($file_ini))
{
$app_type = file($file_ini);
if(!isset($app_type[4]))
{
$app_type = '';
}
else
$app_type = trim($app_type[4]);
if ($app_type == 'form' or $app_type == 'cons') {
$t = explode("_", $app);
$tabela = $t[1];
$check_sql = "SHOW TABLE STATUS LIKE '" . $tabela . "'";
sc_lookup(rs, $check_sql);
if (isset({rs[0][0]})) {
$_a = explode(" ", {rs[0][17]});
if ($app_type == 'form') {
$descri = "Formulário de ". $_a[2];
}
else
$descri = "Consulta ". $_a[2];
}
else
$descri = "";
}
else
$descri = "";
}
else
$app_type = '';
$sql = "SELECT count(*) FROM cadapps WHERE NomApp = '". $app ."' ";
sc_lookup(rs, $sql);
if({rs[0][0]} == 0)
{
$sql = "INSERT INTO cadapps(NomApp, TipApp, DesApp) VALUES ('". $app ."', '".$app_type."', '".$descri."')";
sc_exec_sql( $sql );
}
}
onValidateSuccess:
$html = '';
if(is_array($arr_apps))
{
$total_apps = count($arr_apps);
foreach($arr_apps as $app)
{
$html .= "<br/>". $app;
}
}
{sync} = "<br/><b>". $total_apps. " ". {lang_syncronized_apps} . "</b>" .$html;
Estrutura: Basta você criar um campo chamado “sync” do tipo label, e com a label {lang_sync_apps}. Ordene como quiser.
- Essa aplicação garantirá que nossas novas apps vão para a tabela de aplicações.
2- Após finalizado a parte essencial da Segurança, vamos ao cadastro dos módulos.
No cadastro de módulo eu fiz uma N-N Relations para usar um Duplo-Select e colocar mais de uma app pra um módulo.
- Fazendo a N-N Relations:
2.1 - Crie um formulário baseado na tabela de módulos.
-
Vá no menu da esquerda, N-N Relations e crie um novo.
-
Identificação -> Tipo de Dado: Texto
Label: Aplicações -
Comando Select -> select NomApp, DesApp from cadapps order by DesApp
- A descrição vai vir em branco na sync, a não ser que sua nomenclatura seja: form_nomedatabela sendo assim o meu script colocará Formulário de {DESCRIÇÃO/COMENTÁRIOS DA SUA TABELA NO BANCO}.
Ex.: cadapps - O comentário da tabela no banco é “Cadastro de Aplicações” então quando for sincronizar o form_cadapps a descrição será Formulário de Aplicações. - Só é válido para form e grid.
-
Exibição do Lookup -> Usei Duplo Select com 7 Linhas
-
Tabela de Ligação -> Essa é a tabela que agente criou: “modapps” Módulos/Aplicações
-
Chave de Ligação -> CodMod - Código do Módulo
-
Campo Lookup -> NomApp - Nome da Aplicação
O resto é opcional e eu deixei padrão.
Bom depois da N-N Criada faça alguns cadastros de módulos colocando as aplicações, no meu caso eu combinei a chave nome da app como única, então não terei 2 apps iguais para dois módulos diferentes, mas você pode fazer como quiser.
2.2- Criando a tela de permissões
-
Essa tela é um pouco chata, mas nada muito difícil.
-
Crie um formulário baseado na tabela “modusu”. (Multiplos Registros)
-
Faça com que só mostre os campos de permissão, e o campo CodMod, onde você vai colocar os campos de permissão como checkbox e farão no manual, label vazio (Y) para marcado (N) para desmarcado.
- Lembrem de não colocar Label.
- Faça lookup do codmod para mostrar o nome do módulo.
No onApplicationInit eu peguei a descrição das outras chaves para mostrar no cabeçalho e rodapé:
$str_rev = "SELECT NomRev FROM cadrev WHERE CodRev = ".[glo_revenda];
sc_lookup(rs, $str_rev);
[glo_desc_revenda] = {rs}[0][0];
$str_usu = "SELECT NomUsu FROM cadusu WHERE CodUsu = ".[glo_usuario];
sc_lookup(us, $str_usu);
[glo_desc_usuario] = {us}[0][0];
$str_mod = "SELECT NomUni FROM caduni WHERE CodUni = ".[glo_unidade];
sc_lookup(fs, $str_mod);
[glo_desc_unidade] = {fs}[0][0];
Retire o botão de delete para melhorar o layout e pronto. Recomendo marcarem um radio em configurações da aplicação que se chama “Marcar Todos os Checkbox”, que criará um checkbox que marca todos da página.
2.3- Controle para acessar a tela de permissões
- Crie uma app do tipo controle e crie os seguintes campos:
Revenda
Unidade
Usuário
Eu fiz, Texto-Autocomplete, Select, Texto-Autocomplete respectivamente, mas vai do gosto de cada um.
onApplicationInit:
sc_reset_global([glo_usuario], [glo_revenda], [glo_unidade], [glo_desc_unidade], [glo_desc_revenda], [glo_desc_usuario]);
onValidate:
Aqui eu faço uma coisa muito importante, eu crio os dados da tabela de permissão com o valor defult do banco, “N”. Porque? Pois quando fazemos a sincronia de novas apps e incluímos essas apps no módulo, o registro de permissão dela ainda não existe, então você não teria como dar permissões para essa aplicação, com esse código você nem precisa cadastrar, ao você clicar em Ok, todas as permissões possíveis de acordo com seus módulos já serão criados automaticamente, se dor de cabeça.
[code][glo_usuario] = {usuario};
[glo_revenda] = {revenda};
[glo_unidade] = {unidade};
sc_select(rs, “SELECT CodRev FROM cadrev”);
$arr_rev = array();
while(!$rs->EOF)
{
$arr_rev[] = $rs->fields[0];
$rs->MoveNext();
}
$rs->Close();
foreach($arr_rev as $k => $rev)
{
sc_select(us, “SELECT CodUni FROM caduni WHERE CodRev = $rev”);
$arr_uni = array();
while(!$us->EOF)
{
$arr_uni[] = $us->fields[0];
$us->MoveNext();
}
$us->Close();
foreach($arr_uni as $u => $uni)
{
sc_select(ps, "SELECT CodUsu FROM cadusu");
$arr_usu = array();
while(!$ps->EOF)
{
$arr_usu[] = $ps->fields[0];
$ps->MoveNext();
}
$ps->Close();
foreach($arr_usu as $b => $usu)
{
sc_select(ms, "SELECT CodMod FROM cadmod");
$arr_mod = array();
while(!$ms->EOF)
{
$arr_mod[] = $ms->fields[0];
$ms->MoveNext();
}
$ms->Close();
foreach($arr_mod as $g => $mod)
{
/* VERIRIFICA SE JÁ EXISTE CADASTRO DAS PERMISSÕES */
$check_sql = "SELECT *"
. " FROM modusu"
. " WHERE CodRev = '" . $rev . "'"
. " AND CodUni = '" . $uni . "'"
. " AND CodUsu = '" . $usu . "'"
. " AND CodMod = '" . $mod . "'";
sc_lookup(fs, $check_sql);
if (!isset({fs[0][0]}))
{
$sql = "INSERT INTO modusu(CodRev, CodUni, CodUsu, CodMod) VALUES ('". $rev ."', '".$uni."', '".$usu."', '".$mod."')";
sc_exec_sql( $sql );
}
}
}
}
}[/code]
onValidateSuccess:
sc_redir(form_modusu, "_self");
*Feito isso só falta agora verificar as permissões no login.
3- Permissões no Login
-Pegue sua app de login criada pelo SC e no final da onValidade acrescente:
if ($usr_priv_admin) {
$adm_log = TRUE;
sc_set_global($adm_log);
}
else {
if ({revenda} == '' or {unidade} == '') {
sc_error_message("Escolha a revenda e a unidade desejada!");
}
$adm_log = FALSE;
sc_set_global($adm_log);
}
Isso definirá que quando o usuário tiver privilégios de administrador ele não precisará escolher Revenda e Unidade.
e na onValidateSuccess:
[code]if ([adm_log] == TRUE) {
$sql = "SELECT
modapps.NomApp, modusu.AceMod, modusu.AltMod, modusu.ExcMod,
modusu.ExpMod, modusu.ImpMod, modusu.IncMod
FROM
modapps
JOIN
modusu ON modusu.CodMod = modapps.ModCod
WHERE
modusu.CodUsu = ".[usr_code];
$log_revenda = "MASTER";
$log_unidade = "MASTER";
sc_set_global($log_revenda);
sc_set_global($log_unidade);
}
else {
$sql = "SELECT
modapps.NomApp, modusu.AceMod, modusu.AltMod, modusu.ExcMod,
modusu.ExpMod, modusu.ImpMod, modusu.IncMod
FROM
modapps
JOIN
modusu ON modusu.CodMod = modapps.ModCod
WHERE
modusu.CodUsu = “.[usr_code].”
AND
modusu.CodRev = “.{revenda}.”
AND
modusu.CodUni = ".{unidade};
$sql_log = "SELECT NomUni FROM caduni WHERE CodUni = ".{unidade}." AND CodRev = ".{revenda};
sc_lookup(ds, $sql_log);
$log_unidade = {ds[0][0]};
$sql_fog = "SELECT NomRev FROM cadrev WHERE CodRev = ".{revenda};
sc_lookup(gs, $sql_fog);
$log_revenda = {gs[0][0]};
sc_set_global($log_revenda);
sc_set_global($log_unidade);
}
sc_select(rs, $sql);
if ({rs} !== false)
{
while (!$rs->EOF)
{
if( $rs->fields[1] == ‘Y’ ||
$rs->fields[2] == ‘Y’ ||
$rs->fields[3] == ‘Y’ ||
$rs->fields[4] == ‘Y’ ||
$rs->fields[5] == ‘Y’ ||
$rs->fields[6] == ‘Y’)
{
sc_apl_status($rs->fields[0], ‘on’);
}
else
{
sc_apl_status($rs->fields[0], ‘off’);
}
sc_apl_conf($rs->fields[0], 'access', has_priv($rs->fields[1]));
sc_apl_conf($rs->fields[0], 'insert', has_priv($rs->fields[2]));
sc_apl_conf($rs->fields[0], 'delete', has_priv($rs->fields[3]));
sc_apl_conf($rs->fields[0], 'update', has_priv($rs->fields[4]));
//export
$export_permission = 'btn_display_'. has_priv($rs->fields[5]);
sc_apl_conf($rs->fields[0], $export_permission, 'xls');
sc_apl_conf($rs->fields[0], $export_permission, 'word');
sc_apl_conf($rs->fields[0], $export_permission, 'pdf');
sc_apl_conf($rs->fields[0], $export_permission, 'xml');
sc_apl_conf($rs->fields[0], $export_permission, 'csv');
sc_apl_conf($rs->fields[0], $export_permission, 'rtf');
//export
$export_permission = 'btn_display_'. has_priv($rs->fields[6]);
sc_apl_conf($rs->fields[0], $export_permission, 'print');
$rs->MoveNext();
}
$rs->Close();
sc_log_add('login', {lang_login_ok});
sc_redir('safe_menu');
}[/code]
*Nota: Lembrem-se de trocar os nomes das minhas apps para os da de vocês. E o SQL para o SQL de vocês.
E para ajudar a entender isso tudo deixo uma Demo aqui:
Login: robert
User: master
– Usuário master não precisa de escolher a revenda.
Login: wesley
Senha: 1234
– Só têm acesso na Revenda: EAN Unidade: Serra -> Podem conferir.
Para visualizar app de módulos: Gerenciar>Módulos
Para visualizar app de sincronia: Segurança>Sync
Para visualizar app de permissões: Gerenciar>Permissões
Revenda: EAN
Unidade: Qualquer
Usuário: robert
*NOTA Eu não mostrei criação da app de Revenda nem de Unidade porque varia de cada um, talvez vocês usem Escola/Sala, Empresa/Filial, Loja/Departamento, é só enquadra na sua necessidade.
-Agradecimento especial ao Saulo Borges e Haroldo Passos por terem me ajudado bastante a entender o scriptcase.