Question:
My system performs client authentication (it's a web service) via BaseCertLoginModule
, a JBoss class based on the JaaS specification. My project actually extends this class, and this extension is called on each request to decide whether or not the user has access to the web service. I do this by taking the request serial (two-way SSL) and doing a database lookup with that serial.
This whole thing (authentication) is working fine. What I wanted now is to resolve the authorization. I wanted to use the @RequiredRole
annotation, as I already define user roles in my LoginModule
. I thought it would be just to do this:
@Override
@WebMethod
@RequiredRole("MASTER")
public void cancelarLaudo(CancelamentoLaudoRequest cancelamentoLaudoRequest)...
Where cancelarLaudo
is an operation of my SOAP web service.
But it did not work. 🙂
What else would I have to do? I've already read the Demoiselle documentation, but it's not clear how I would make it work alongside the LoginModule
scheme I already have.
My LoginModule
:
package br.gov.serpro.sislvws.security.loginmodule;
import java.security.Principal;
import java.security.acl.Group;
import java.security.cert.X509Certificate;
import javax.persistence.NoResultException;
import javax.security.auth.login.LoginException;
import org.jboss.logging.Logger;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.spi.BaseCertLoginModule;
import br.gov.frameworkdemoiselle.util.Beans;
import br.gov.serpro.sislv.entity.CertificadoDigital;
import br.gov.serpro.sislv.entity.Entidade;
import br.gov.serpro.sislv.persistence.EntidadeDAO;
public class SislvLoginModule extends BaseCertLoginModule {
private Logger logger = Logger.getLogger(this.getClass());
private EntidadeDAO entidadeDAO;
public SislvLoginModule() {
entidadeDAO = Beans.getReference(EntidadeDAO.class);
}
@Override
protected Principal createIdentity(String arg0) throws Exception {
X509Certificate certificate = (X509Certificate) getCredentials();
try {
Principal principal = new SislvPrincipal(certificate);
log.info("Usuário identificado: " + principal);
return principal;
} catch (Exception e) {
String message = "Falha ao tentar autenticar o certificado " + certificate.toString();
logger.error(message, e);
throw e;
}
}
@Override
protected Group[] getRoleSets() throws LoginException {
CertificadoDigital certificado = certificado();
try {
Entidade entidade = entidadeDAO.findBy(certificado);
String role = entidade.getTipoEntidade().toString();
SimpleGroup roles = new SimpleGroup("Roles");
SimplePrincipal user = new SimplePrincipal(role);
roles.addMember(user);
return new Group[] { roles };
} catch (NoResultException e) {
String msg = certificado + " não autorizado a acessar o web service.";
logger.error(msg);
throw new LoginException(msg);
} catch (Exception e) {
logger.error("Erro inesperado durante a autenticação", e);
throw new LoginException();
}
}
private CertificadoDigital certificado() throws LoginException {
SislvPrincipal identity = null;
try {
identity = (SislvPrincipal) getIdentity();
CertificadoDigital cert = new CertificadoDigital();
cert.setCommonName(identity.getCommonName());
cert.setSerial(identity.getSerial());
cert.setCommonNameEmissor(identity.getCommonNameEmissor());
return cert;
} catch (Exception e) {
logger.error("Erro inesperado durante a autenticação", e);
throw new LoginException();
}
}
}
JBoss configuration to enable LoginModule
:
<security-domain name="SislvSecurityDomain">
<authentication>
<login-module code="br.gov.serpro.sislvws.security.loginmodule.SislvLoginModule" flag="required">
<module-option name="verifier" value="org.jboss.security.auth.certs.AnyCertVerifier" />
</login-module>
</authentication>
...
Answer:
Oops, the main thing missing was enabling the RequiredRoleInterceptor
interceptor in beans.xml
. Also, I've extended Demoiselle's ServletAuthorizer
class to implement the hasRole
method. And finally, this class was registered in demoiselle.properties.
Now the solution is working, but I still wanted to improve one aspect.
Here is my authorizing class:
package br.gov.serpro.sislvws.ws.autorizacao;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import br.gov.frameworkdemoiselle.security.ServletAuthorizer;
import br.gov.serpro.sislvws.security.loginmodule.RequestAutenticado;
@RequestScoped
public class RoleBasedAuthorizer extends ServletAuthorizer {
private static final long serialVersionUID = 1L;
@Inject
private RequestAutenticado requestAutenticado;
@Override
public boolean hasRole(String role) throws Exception {
String tipoEntidade = requestAutenticado.getEntidadeCliente().getTipoEntidade().toString();
return tipoEntidade.equals(role);
}
}
In this solution, there is a repetition of the logic to define the role of the logged "user". This logic appears in getRoleSets
from the login module and in the hasRole
of the authorizer. What I would find better is if the role definition was only the login module, and if the authorizer I could retrieve which role was already defined there in the login module.
I tried queries like "(jboss|jaas) retrieve role define on login module", but still can't solve it.