terça-feira, 14 de julho de 2020

Contas Pagar/Receber - Parte XVI - Autorização

Introdução
Nesse décimo sexto post vou mostrar como configurar a autorização utilizando CDI. Vou definir um nivel de acesso nas telas através do tipo de cargo do usuário. Vou mostrar também como redirecionar para a página de login quando a sessão estiver expirada. Por default a sessão é expirada após 30 minutos de inatividade, mas esse tempo pode ser configurado via web.xml (estático) ou via programação (dinâmico). Vou deixar configurado no web.xml para a sessão expirar em 5 minutos. Mas para usuários com cargo de Diretor ou Gerente vou colocar pra expirar em 1 minuto. Quando a sessão estiver expirada vou mostrar uma mensagem e redirecionar para a página de login. 

Configuração
Vou deixar configurado então no web.xml o tempo de 5 minutos para expiração da sessão colocando a seguinte tag:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="WebApp_ID" version="3.0">

<display-name>contas</display-name>

<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>

<session-config>
<session-timeout>5</session-timeout>
</session-config>

</web-app>
Agora vou configurar para os cargos de Diretor ou Gerente para expirar em 1 minuto. Vou fazer isso no evento de após o login:
LoginObserver.java:

public class LoginObserver {

private void login(@Observes @JArchEventLoginAfter UsuarioEntity usuario, AlertaFacade alertaFacade, MensagemFacade mensagemFacade) {
alertaFacade.atualizaAlertaSessao();
mensagemFacade.atualizaMensagemSessao();

if (usuario.getCargo().equals(CargoType.DIRETOR) || usuario.getCargo().equals(CargoType.GERENTE)) {
JsfUtils.getSession().setMaxInactiveInterval(60);
}
}
}
O parâmetro 60 utilizado no método setMaxInactiveInterval corresponde aos segundos, ao contrário do web.xml que recebe o valor em minutos.
Agora vou configurar o listener para quando a sessão expirar, exibir a mensagem de sessão expirada e redirecionar para a tela de login. Para isso vou criar um listener para escutar as fases do JSF.
JsfFaseListener.java:

public class JsfFaseListener {

private final void verificaSessaoExpirada(@Observes @JArchJsfEventAfter @JArchJsfEventRestoreView PhaseEvent event) {

boolean login = !event.getFacesContext().getViewRoot().getViewId().contains(".login.xhtml");
HttpSession session = ((HttpSession)event.getFacesContext().getExternalContext().getSession(false));

if ((session == null && !login) || (session != null && session.isNew())) {
JavaScriptUtils.showMessageBodyErrorRedirect("../login/login.jsf",
BundleUtils.messageBundle("message.sessaoExpirada"),
BundleUtils.messageBundle("message.sessaoExpirada"));
}
}
}
Republicando a aplicação e logando com o usuário que é diretor ou gerente e após um minuto de inatividade:

Autorização
Agora vou definir as seguintes regras de autorização, operador não pode alterar e nem excluir registros. Vou criar um observer para não permitir alteração e exclusão quando o usuário logado for Operador:
AutorizacaoObserver.java:

public class AutorizacaoObserver {

private void validaAlteracaoViaOperador(@Observes @JArchEventValidChange CrudMultiTenantEntity entity, UserInformation userInformation) {
UsuarioEntity usuarioSistema = userInformation.get(UsuarioEntity.class);

if (UsuarioEntity.class.isAssignableFrom(entity.getClass()) && usuarioSistema.equals(entity)) {
return;
}

if (usuarioSistema.getCargo() == CargoType.OPERADOR) {
throw new ValidationException(BundleUtils.messageBundle("message.naoAutorizadoEntreContatoSupervisor"));
}
}

private void validaExclusaoViaOperador(@Observes @JArchEventValidDelete CrudMultiTenantEntity entity, UserInformation userInformation) {
UsuarioEntity usuarioSistema = userInformation.get(UsuarioEntity.class);

if (usuarioSistema.getCargo() == CargoType.OPERADOR) {
throw new ValidationException(BundleUtils.messageBundle("message.naoAutorizadoEntreContatoSupervisor"));
}
}

}
Repare que a entidade que está sendo observado é o CrudMultiTenantEntity,  essa classe é a classe pai de nossas entidades, então o evento será disparado para qualquer entidade que for herdada dela. Agora vou adicionar a mensagem no bundle.
bundle_pt_BR.properties:

message.naoAutorizadoEntreContatoSupervisor=N\u00E3o autorizado. Entre com contato com seu Supervisor

Com essa alteração agora qualquer tentativa de alteração ou exclusão com um usuário logado Operador:

Na tentativa de alteração acima foi criticado. E ao tentar excluir:
A mesma crítica e mostrada.

Bloqueando Acesso a Ação
A solução acima garante que não será efetuado nenhuma alteração ou exclusão pelo usuário logado com cargo de Operador. Mas posso melhorar issom bloquendo o acesso de ações que seja diferente de Incluir, Clonar, Consultar e Imprimir. Uma forma de fazer isso seria alterar todas as telas de lista de todas as funcionalidades colocando uma EL, mas isso demandaria alterar todos os xhtml's de lista. Então para evitar de ter que alterar todos os xhtml's, vou utilizar um recurso do CDI junto com o JARCH o interceptor. Vou criar um interceptor para quando o usuário acessar qualquer tela de lista, vai bloquear todas as ações que não seja Incluir, Clonar, Consultar e Imprimir. Segue a implementação.
AutorizacaoInteceptor.java:

@Interceptor
@JArchCrudList
@Priority(Interceptor.Priority.APPLICATION)
public class AutorizacaoInterceptor implements Serializable {

@Inject
private UserInformation userInformation;

@PostConstruct
public void interceptaPosConstruct(InvocationContext context) throws Exception {

context.proceed(); //chama o PosConstruct interceptado

UsuarioEntity usuarioLogado = userInformation.get(UsuarioEntity.class);

if (usuarioLogado.getCargo() != CargoType.OPERADOR) {
return;
}

ICrudListAction crudListAction = (ICrudListAction) context.getTarget();

crudListAction.getAcessMenu().disableAllItemMenuAction();
crudListAction.getAcessMenu().enableItemMenuAction(ActionCrudType.INSERT); crudListAction.getAcessMenu().enableItemMenuAction(ActionCrudType.CLONE);
crudListAction.getAcessMenu().enableItemMenuAction(ActionCrudType.CONSULT);
crudListAction.getAcessMenu().enableItemMenuAction(ActionCrudType.PRINT);
}
}
Agora qualquer tela que entrar como operador somente as ações de Incluir, Clonar, Consultar e Imprimir estarão habilitadas. 

Conclusão
Nesse post mostrei como configurar o tempo de sessão do usuário. Ajustei o tempo de sessão dos cargos Diretor e Gerente para 1 minuto de forma dinâmica. Crei um listener de fase do JSF para dar a mensagem de sessão expirada e redirecionar para a página de login. Criei um observer para bloquear qualquer tipo de alteração ou exclusão quando logado como Operador. E por último mostrei o recurso de interceptor para bloquear as opções de ações não permitidas para o Operador.  

Segue o link dessa video aula: https://youtu.be/pdpc13au1gs

Até mais,

Nenhum comentário:

Postar um comentário

Versão 23.3.0-Final

      Introdução Nesse post vou mostrar as principais novidades da versão 23.3.0, algumas correções e pequenas alterações. Alterações Além d...