Controle de Acesso

Projeto [x]Harbour - Compilador de código aberto compatível com o Clipper.

Moderador: Moderadores

Avatar do usuário
Marcos
Usuário Nível 3
Usuário Nível 3
Mensagens: 355
Registrado em: 20 Set 2003 09:16
Localização: Cáceres/Mato Grosso

Controle de Acesso

Mensagem por Marcos »

Estou abrindo este tópico porque após várias pesquisas aqui no fórum não achei nada relacionado, apenas entradas com Senhas, mas me refiro aqui a controles de acesso com permissões a determinadas telas, o que o usuário pode ou não pode fazer dentro do sistema, como usuário de sistema, já vi vários controles de formas diferentes, muitos até ruim de usar, outros sistemas com remendos, ou seja, pensou num controle e o sistema foi criado, depois não foi atendido 100%, assim remendaram o sistema dando permissões por botões, e ficou um saco para o usuário, portanto a minha pergunta a todos, qual a melhor forma de criar um controle de acesso para não se arrepender no futuro ?
Atenciosamente,
_____________________________
Marcos Antonio da Silva
marcosilva90@hotmail.com
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Controle de Acesso

Mensagem por JoséQuintas »

É uma questão complicada essa.
Aqui faço tudo baseado em opções do menu, e cada um tem seu nome.
A configuração de acessos tem a cópia do menu pra escolher entre sim/não, por usuário.

Qualquer dia adiciono opção de grupos, pra configurar acesso de um grupo, e cadastrar usuários em determinados grupos.

Há pouco tempo acrescentei a opção do usuário *.*, que define opções de acesso geral.
A checagem é simples: if TemAcesso("nomeopcao")

Código: Selecionar todos

FUNCTION TemAcesso( cNomeOpcao )
   LOCAL lTemAcesso := .T.
   IF .NOT. Encontra( AppUserName() + cNomeOpcao, "senhas" )
       IF .NOT. Encontra( "*.*" + cNomeOpcao, "senhas" )
            lValue := .F.
      ENDIF
   ENDIF
   RETURN lTemAcesso
Tenho uma opção especial no menu, aonde acrescento opções não visíveis, só pra usar a mesma rotina de configuração.
Por exemplo, se pode confirmar pedido sem saldo em estoque, idem com cliente devendo, etc.

Então na configuração é escolher o usuário, e nas opções do menu escolher entre sim/não.
Nessa mesma configuração dá pra fazer o contrário: a partir de uma opção ver/configurar usuários que podem acessar.

Para o que falei sobre grupo, faltaria eu criar a opção de criar grupos e usuários de cada grupo.
A checagem do grupo substituiria o "*.*" , sendo que "*.*" seria o grupo geral.

A checagem não é o problema, mas sim a configuração ficar fácil pro usuário.
Por eu usar uma cópia no estilo do menu, acaba sendo no estilo que estão acostumados.
Como meu menu é um array, a rotina de configuração apenas usa o array do menu pra permitir configurar.

Tenho lá:
- meu usuário - ele é nível máximo, e não depende de configuração
- usuário administrador - a única diferença é ter acesso ao módulo de configurar usuários, e acaba tendo acesso a algo mais
- usuário comum - só tem acesso ao que liberarem pra ele
- *.* - equivale a "qualquer um". liberou pra este liberou pra todos

E o menu... se o usuário não tiver acesso a determinada opção, ela nem aparece.

Não é dos melhores, mas tá funcionando e não requer nenhuma mudança ao criar novas opções.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
ANDRIL
Usuário Nível 5
Usuário Nível 5
Mensagens: 1299
Registrado em: 06 Jul 2004 00:44
Contato:

Controle de Acesso

Mensagem por ANDRIL »

Marcos, esta é uma questão bem complexa. Apanhei muito ate achar um método menos doloroso.

Quanto a "remendo" alterações sempre podem ocorrer em sistemas que tem constantes atualizações, pois um novo módulo ou opção requer uma nova permissão. Exemplo, o usuário pode ter acesso a listagem de produtos e por necessidade foi criado um novo módulo de inclusão de "observações" abrindo um novo acesso a ser controlado.

Segui a seguinte lógica. Criei um dbf exclusivo com 2 campos, onde o primeiro chama-se LINHA tem +- 80 caracteres, o segundo PERMISSAO com 1 caracter onde constam as descrições dos acessos conforme abaixo:

Código: Selecionar todos

#MENU VENDAS
001 -  Pode vender......................................................[S]
002 -     vender a dinheiro.............................................[S]
003 -     vender a cartao...............................................[N]
004 -  Cancelar venda..................................................[N]
005 -  Acesso a Orcamentos Gravados...............................[S]
006 -      registrar orcamento.........................................[S]
007 -      apagar orcamento...........................................[N]
....
100 -  Excluir dados....................................................[S]  
No cadastro do usuario defini um campo de 500 carateres que é automaticamente preenchido com "N" ao cadastra-lo. Cada posicao neste campo corresponde ao numero
inicial conforme acima, 001, 002 etc. Para saber se o usuario tem ou nao acesso criei uma função que indica qual é a parte do sistema onde o usuario esta e faz a busca na tabela acima.

Por exemplo, ao entrar na tela de vendas,

Código: Selecionar todos

if tempermissao("001")!=.t.
return .f.
endif
No meu caso ao entrar no sistema o usuario se loga, entao o sistema ja tem o seu codigo. Na funcao tempermissao() eu faço uma busca na tabela do usuario pelo codigo dele, acho o campo de 500 caracteres e vejo o parametro recebido, no exemplo, "001" então verifico na primeira posicao do campo se esta marcado
com SN.

Código: Selecionar todos

function tempermissao(setor)
** nusuario é o codigo do usuario recebido no login, ela deve ser global/public
LOCAL retorno:=.f.

select ("usuarios")
LOCATE  FOR usuario=nusuario 
if found()
    if substr(campo500,val(setor),1)="S"
    retorno:=.t.
    endif 
endif
return retorno
Outra facilidade que acho é a manutenção e criação de novas opções, posso editar colocando novos textos usando o COPY TO com opção [SDF] para criar uma versao texto das permissoes e após minhas alterações importo o novo conteudo para o dbf usando APPEND FROM com opção [SDF].

Essa é minha dica, não ache que será fácil, mapear todas as telas de um sistema e depois fazer ajustes e manutenção dá muito trabalho.

Boa sorte.
Clipper 5.2e / Blinker 5.1 / Harbour 3.2 / GTwvg
Avatar do usuário
Marcos
Usuário Nível 3
Usuário Nível 3
Mensagens: 355
Registrado em: 20 Set 2003 09:16
Localização: Cáceres/Mato Grosso

Controle de Acesso

Mensagem por Marcos »

Já deu para se ter uma ideia, obrigado pela colaboração.
Atenciosamente,
_____________________________
Marcos Antonio da Silva
marcosilva90@hotmail.com
Avatar do usuário
Poka
Usuário Nível 4
Usuário Nível 4
Mensagens: 563
Registrado em: 25 Out 2004 21:26
Localização: Leme/SP

Controle de Acesso

Mensagem por Poka »

Marcos,

No sistema antigo em clipper, tinha algo parecido com a idéia do Andril. Depois que passei para Harbour, estou até hoje para implementar , e não deu certo.
Já vi sistemas que colocam nivel 1 ,nivel 2 de acessos , talvez fique + facil , mas nem faria assim acho que não resolve.
Pretendo fazer da seguinte maneira.

Primeiro:

Todos os cadastros tem que ter permissão de Inclusão, Alteração,Exclusão e Consulta ( se não pode consultar, o resto já tá definido que não pode.
Em relatórios também definir se pode imprimir ou só visualizar. Um vez em uma empresa o funcionário foi mandado embora, mas antes imprimiu todos os clientes para levar no concorrente. Determinados funcionários por exemplo não pode acessar relatórios financeiros.

Segundo:

criar um arquivo de direitos e definir tudo o que pode e o que não pode para cada tipo.
por exemplo:
Gerente de contas a receber
Auxiliar de Contas a receber.
Auxiliar de vendas
etc.

Terceiro:
criar um arquivo das senhas/usuários.
ao cadastrar o usuário, definir qual tabela de direitos utilizar.

Não definir as permissões para cada usuário, daria muito trabalho.


Poka.
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Controle de Acesso

Mensagem por JoséQuintas »

Eu deixo por conta do usuário administrativo.
No caso de um novo vendedor, basta cadastrar o novo vendedor, e "importar" os acessos de outro vendedor qualquer.
Só pra ilustrar, a minha rotina de configuração de senhas.
senhas.png
Como eu disse, não é das melhores, mas tem atendido.
É tudo na base do Sim ou Não, podendo liberar opção, ou opção + submenus.
No último nível, se der um ENTER a mais, aparece essa opção de adicionar/remover usuários à opção atual.
E tudo retirado automaticamente do menu em array.

o menu principal é montado assim:

Código: Selecionar todos

MenuOption( "Movto" )
   MenuDrop()
   MenuOption( "Pedidos/Notas Fiscais" )
      MenuDrop()
      MenuOption( "Orçamentos/Pedidos", "P0600PED" )
      MenuOption( "Emissor JPA", "PEMISSOR" )
      MenuOption( "Nota Fiscal (Serviços)", "PNOT0010" )
      MenuOption( "Consulta a Notas Fiscais", "PNOT0020" )
e o menu interno, dos pedidos por exemplo, assim:

Código: Selecionar todos

   IF TemAcesso( "ADMPEDNOT" )
      AAdd( oFrm:acMoreOptions, "<N>NFCupom" )
      AAdd( oFrm:acMoreOptions, "<W>VerPDF" )
   ENDIF
   IF TemAcesso( "ADMPEDCTE" )
      AAdd( oFrm:acMoreOptions, "<T>CTE" )
   ENDIF
   IF TemAcesso( "ADMPEDGAR" )
      AAdd( oFrm:acMoreOptions, "<B>CodBarras" )
   ENDIF
e algumas opções extras, assim:

Código: Selecionar todos

   IF jpclista->csBloqueio $ "01"
      IF TemAcesso( "ADMPEDBLO" )
         IF .NOT. MsgYesNo( "Cliente com Status " + Trim( jpclista->csDescri ) + " não autorizada a confirmação" + HB_EOL() + ;
            "Usuário atual consegue autorizar. Continua?" )
            RETURN .F.
         ENDIF
         GravaOcorrencia( "JPPEDI", jppedi->pdPedido, "Autorizado pra cliente com status " + Trim( jpclista->csDescri ) )
      ELSE
         MsgStop( "Cliente com Status " + Trim( jpclista->csDescri ) + " não autorizada a confirmação" )
         RETURN .F.
      ENDIF
   ELSEIF jpclista->csBloqueio == "2"
      MsgWarning( "Cliente com Status " + Trim( jpclista->csDescri ) )
   ENDIF
No caso destas configurações extras, elas fazer parte do menu, mas não aparecem no menu do usuário.
Foi a forma de encontrei pra configuração de senhas continuar a mesma automática.
A única diferença entre o menu e a configuração de senhas é que o primeiro menu também fica na vertical na configuração.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
Marcos
Usuário Nível 3
Usuário Nível 3
Mensagens: 355
Registrado em: 20 Set 2003 09:16
Localização: Cáceres/Mato Grosso

Controle de Acesso

Mensagem por Marcos »

As ideias são bem interessante, muito enriquecedoras, penso em fazer por telas, ou seja, permitir ou não a tela que o usuário terá acesso, mas daí vem a questão, e se determinado usuário pode Incluir um Cliente e não excluir?! Neste caso as permissões deveriam ser por botões, aumentando o stress do usuário ao dar as permissões.
Atenciosamente,
_____________________________
Marcos Antonio da Silva
marcosilva90@hotmail.com
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Controle de Acesso

Mensagem por JoséQuintas »

Tem caso onde crio a opção duas vezes, por exemplo, clientes é PCADASTRO, então crio PCADASTROB pra versão limitada.
No meu caso, o nome do fonte já sai do menu.
Em fonte, acrescento essas opções no menu, que ficam invisíveis ao usuário, e a configuração já resolve.

Código: Selecionar todos

PROCEDURE MenuPrincipal
     ...
    IF Click()
        m_Prog := NomeQueTemNoMenu
        SAVE SCREEN TO xTela
        CLS
        @ 0, 0 SAY Padc( "TextoQueTemNoMenu", MaxCol() )
        @ 1, 0 SAY Replicate( Chr(196), MaxCol() )
        DO( m_Prog )
        RESTORE SCREEN FROM xTela
   ENDIF
   RETURN

PROCEDURE PCADASTRO
   IF m_Prog == "PCADASTRO"
       oFrm:acOpcoes := "IAEC" // inclui/altera/exclui/consulta
   ELSE
      oFrm:acOptions := "C" // só consulta
   ENDIF
  ...

PROCEDURE PCADASTROB
   DO PCADASTRO
   RETURN
Como recurso extra, criei uma opção pra digitar usuário,senha e um código de liberação.
Caso tenha esquecido de liberar alguma coisa, isso já libera pro usuário.
Facilita na hora de resolver, ou pra novos módulos.

O negócio é começar a fazer. Conforme for usando, vai vendo o que vai precisando.
Não precisa querer resolver tudo logo na primeira rotina, pode se complicar e acabar não resolvendo nada.

Em outros casos, crio CLIENTEINC, CLIENTALT, CLIENTEXC, e configuro pra essas opções, usando internamente igual mostrei no exemplo de pedidos.

Em um cliente a necessidade foi a primeira opção, já em outro foi a segunda.
A gente vai ajustando conforme vai acontecendo.
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Controle de Acesso

Mensagem por JoséQuintas »

Minha rotina de configuração, note que é tudo manual, nem prompt eu uso, e trata também mouse, por isso tá grande (rs...).
É a vantagem de usar um array do menu, e aproveitar na configuração.
No final, só salvar nas senhas o resultado do array.

Código: Selecionar todos

FUNCTION BoxAcesso( mLini, mColi, mMenuOpt, mOpc, mTitulo, mSaiSetas )
   LOCAL mCont, mLinf, mColf, mTecla, mReturn
   mLinf := mLini + Len( mMenuOpt ) + 2
   IF mLinf > MaxRow() - 4
      mLini := mLini + MaxRow() - 4 - mLinf
      mLinf := mLini + Len( mMenuOpt ) + 2
   ENDIF
   mColf := mColi + 42
   WOpen( mLini, mColi, mLinf, mColf, mTitulo )
   Mensagem( "Selecione e tecle <ENTER>, <S> (Tem Acesso), <N> (Sem Acesso), <ESC> sai" )
   DO WHILE .T.
      FOR mCont = 1 TO Len( mMenuOpt )
         @ mLini + 1 + mCont, mColi + 1 SAY " " + Pad( mMenuOpt[ mCont, 1 ], 31 ) + iif( Len( mMenuOpt[ mCont, 2 ] ) > 0, Chr(16), " " ) + " " + ;
            iif( mMenuOpt[ mCont, 4 ], "SIM", "NAO" ) + " " COLOR iif( mCont == mOpc, SetColorFocus(), SetColorBox() )
      NEXT
      SetColor( SetColorNormal() )
      mTecla := Inkey(0)
      DO CASE
      CASE mTecla == K_ESC
         EXIT
      CASE mSaiSetas .AND. ( mTecla == K_RIGHT .OR. mTecla == Asc( "6" ) .OR. mTecla == K_LEFT .OR. mTecla == Asc( "4" ) ) // setas
         EXIT
      CASE mTecla == K_LBUTTONDOWN
         IF MROW() > mLini + 1 .AND. MROW() < mLini + 2 + Len( mMenuOpt ) .AND. MCOL() > mColi .AND. MCOL() < mColi + 38
            mOpc := MROW() - mLini - 1
            Keyboard Chr( K_ENTER )
         ENDIF
      CASE mTecla == K_RBUTTONDOWN
         Keyboard Chr( K_ESC )
      CASE mTecla == K_DOWN .OR. mTecla == Asc( "2" )
         mOpc := iif( mOpc == Len( mMenuOpt ), 1, mOpc + 1 )
      CASE mTecla == K_UP .OR. mTecla == Asc( "8" )
         mOpc := iif( mOpc == 1, Len( mMenuOpt ), mOpc - 1 )
      CASE mTecla == K_HOME .OR. mTecla == Asc( "7" )
         mOpc := 1
      CASE mTecla == K_END .OR. mTecla == Asc( "1" )
         mOpc := Len( mMenuOpt )
      CASE mTecla == Asc( "S" ) .OR. mTecla == Asc( "s" )
         MudaAcess( mMenuOpt, mOpc, .T. )
      CASE mTecla == Asc( "N" ) .OR. mTecla == Asc( "n" )
         MudaAcess( mMenuOpt, mOpc, .F. )
      CASE mTecla == K_ENTER
         IF Len( mMenuOpt[ mOpc, 2 ] ) > 0
            // se tem submenus, abre para os submenus
            mMenuOpt[ mOpc, 4] := BoxAcesso( mLini + 1 + mOpc, mColi + 5, mMenuOpt[ mOpc, 2 ], 1, mMenuOpt[ mOpc, 1 ], .T. )
         ELSE
            // se não tem submenus, então só resta permitir por usuário
            PCFG0050User( mMenuOpt[ mOpc, 3 ] )
         ENDIF
      ENDCASE
   ENDDO
   mReturn := .F.
   FOR mCont = 1 TO Len( mMenuOpt )
      IF mMenuOpt[ mCont, 4 ]
         mReturn := .T.
      ENDIF
   NEXT
   WClose()
   RETURN mReturn

// pra modificar a opção, ou todos os submenus da opção 
FUNCTION MudaAcess( mMenuOpt, mOpc, mModo )
   LOCAL mCont

   IF ValType( mMenuOpt[ mOpc, 3 ] ) != "B"
      mMenuOpt[ mOpc, 4 ] := mModo
      IF Len( mMenuOpt[ mOpc, 2 ] ) > 0
         FOR mCont = 1 TO Len( mMenuOpt[ mOpc, 2 ] )
            MudaAcess( mMenuOpt[ mOpc, 2 ], mCont, mModo )
         NEXT
      ENDIF
   ENDIF
   RETURN NIL
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Avatar do usuário
JoséQuintas
Administrador
Administrador
Mensagens: 20267
Registrado em: 26 Fev 2007 11:59
Localização: São Paulo-SP

Controle de Acesso

Mensagem por JoséQuintas »

A tela gerada por essa rotina
Anexos
senhas.png
José M. C. Quintas
Harbour 3.2, mingw, gtwvg mt, fivewin 25.04, multithread, dbfcdx, MySQL, ADOClass, PDFClass, SefazClass, (hwgui mt), (hmg3), (hmg extended), (oohg), PNotepad, ASP, stored procedure, stored function, Linux (Flagship/harbour 3.2)
"The world is full of kings and queens, who blind our eyes and steal our dreams Its Heaven and Hell"

https://github.com/JoseQuintas/
Responder