diff --git a/backoffice/pom.xml b/backoffice/pom.xml index 39120f900a10252cc7d5711d9036a80695b53c40..60caaaa200d4c4201ac907dd427180d265f17261 100644 --- a/backoffice/pom.xml +++ b/backoffice/pom.xml @@ -51,6 +51,12 @@ <artifactId>wildfly-jsf</artifactId> </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + </dependency> + + <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> diff --git a/backoffice/src/main/java/uy/edu/fing/tse/jsf/UserLoginBean.java b/backoffice/src/main/java/uy/edu/fing/tse/jsf/UserLoginBean.java index 20e8e9b70e7f922d1f7522176b46567f73bf7f33..2844978f65672bd75739542ac3116180225395a9 100644 --- a/backoffice/src/main/java/uy/edu/fing/tse/jsf/UserLoginBean.java +++ b/backoffice/src/main/java/uy/edu/fing/tse/jsf/UserLoginBean.java @@ -6,12 +6,14 @@ import uy.edu.fing.tse.central.business.BusinessLocal; import uy.edu.fing.tse.central.business.security.SecurityLocal; import uy.edu.fing.tse.dto.Role; import uy.edu.fing.tse.dto.UserBO; +import uy.edu.fing.tse.jsf.session.SessionBean; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.enterprise.context.RequestScoped; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; +import javax.inject.Inject; import javax.inject.Named; import java.io.Serializable; import java.util.List; @@ -25,9 +27,12 @@ public class UserLoginBean implements Serializable { private static final Logger LOG = LoggerFactory.getLogger(UserLoginBean.class); @EJB private SecurityLocal securityLocal; + @EJB private BusinessLocal negocio; + @Inject + private SessionBean session; private List<Role> roles; @@ -40,8 +45,6 @@ public class UserLoginBean implements Serializable { private String repassword; - private String token; - public List<Role> getRoles() { return roles; } @@ -62,31 +65,21 @@ public class UserLoginBean implements Serializable { this.repassword = repassword; } - public String getToken() { - return token; - } - public void setToken(String token) { - this.token = token; - } - - public String login() { - token = securityLocal.login(user); + public void login() { + String token = securityLocal.login(user); if (token != null) { - try {//TODO sacar - Thread.sleep(2000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return "gestionhechos"; + session.setToken(token); + session.setUser(user); + // return "gestionhechos"; } else { FacesContext.getCurrentInstance().addMessage( null, new FacesMessage(FacesMessage.SEVERITY_WARN, "Ingreso incorrecto", "Por favor verificar los datos ingresados")); - return ""; + // return ""; } } diff --git a/backoffice/src/main/java/uy/edu/fing/tse/jsf/security/JwtFilter.java b/backoffice/src/main/java/uy/edu/fing/tse/jsf/security/JwtFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..e71a94e2a1602a942807c8dec0aee2c70d3999a0 --- /dev/null +++ b/backoffice/src/main/java/uy/edu/fing/tse/jsf/security/JwtFilter.java @@ -0,0 +1,47 @@ +package uy.edu.fing.tse.jsf.security; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; + +import javax.servlet.*; +import javax.servlet.annotation.WebFilter; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@WebFilter("/jsf/*") +public class JwtFilter implements javax.servlet.Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + + } + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + final HttpServletRequest request = (HttpServletRequest) req; + final HttpServletResponse response = (HttpServletResponse) res; + + final String authHeader = request.getHeader("Authorization"); + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + //TODO se puede hacer un send Redirect para enviarlo al Login + response.setStatus(401); + return; + } + + try { + final String token = authHeader.substring(7); // The part after "Bearer " + final Claims claims = Jwts.parser().setSigningKey("1q2w3e4r5t6y7u8i9o0p").parseClaimsJws(token).getBody(); + request.setAttribute("claims", claims); + } catch (final Exception e) { + response.setStatus(401); + return; + } + + chain.doFilter(req, res); + } + + @Override + public void destroy() { + + } +} diff --git a/backoffice/src/main/java/uy/edu/fing/tse/jsf/session/SessionBean.java b/backoffice/src/main/java/uy/edu/fing/tse/jsf/session/SessionBean.java new file mode 100644 index 0000000000000000000000000000000000000000..ae950252835b231acebd35924b5006d524fbac91 --- /dev/null +++ b/backoffice/src/main/java/uy/edu/fing/tse/jsf/session/SessionBean.java @@ -0,0 +1,44 @@ +package uy.edu.fing.tse.jsf.session; + +import uy.edu.fing.tse.dto.UserBO; + +import javax.enterprise.context.SessionScoped; +import javax.faces.context.FacesContext; +import javax.inject.Named; +import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; + +@SessionScoped +@Named("sessionBean") +public class SessionBean implements Serializable { + + private static final long serialVersionUID = 9062153372176877309L; + + private String token = null; + + private UserBO user = null; + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public UserBO getUser() { + return user; + } + + public void setUser(UserBO user) { + this.user = user; + } + + public void putToken() { + if (token == null) { + return; + } + HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); + response.setHeader("Authorization", "Bearer " + token); + } +} diff --git a/backoffice/src/main/webapp/WEB-INF/templates/template.xhtml b/backoffice/src/main/webapp/WEB-INF/templates/template.xhtml index 0f091e85ed540dddf7a2466706758107e35f5b6a..624423b2d086142fd70dab5f8e32a8c80ac1cdb3 100644 --- a/backoffice/src/main/webapp/WEB-INF/templates/template.xhtml +++ b/backoffice/src/main/webapp/WEB-INF/templates/template.xhtml @@ -2,9 +2,34 @@ <html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" - xmlns:h="http://xmlns.jcp.org/jsf/html" + xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:f="http://xmlns.jcp.org/jsf/core" > <h:head> + <style type="text/css"> + .ui-datatable .ui-datatable-header { + text-align: right !important; + } + + .ui-button-text-only .ui-button-text { + padding: 0.3em 0.4em; + } + + .ui-selectcheckboxmenu-panel .ui-selectcheckboxmenu-header .ui-chkbox { + visibility: hidden; + } + + .ui-filter-column .ui-column-customfilter .custom-filter { + width: 100%; + box-sizing: border-box; + } + + .year-spinner input { + width: 100%; + box-sizing: border-box; + } + + + </style> <title>BackOffice - feiknius</title> </h:head> @@ -15,6 +40,7 @@ </header> <!-- Menús, headers y todo lo que vaya antes del contenido --> <p:messages/> + <f:event type="preRenderView" listener="#{sessionBean.putToken()}"/> <ui:insert name="contenido"> Contenido por defecto para que no quede en blanco... </ui:insert> diff --git a/backoffice/src/main/webapp/index.xhtml b/backoffice/src/main/webapp/index.xhtml index 08b9fd6596987ecfb506667ed31ea04dea6fa4de..6f965e39f3b93b0f185f13985618e98ac9571d66 100644 --- a/backoffice/src/main/webapp/index.xhtml +++ b/backoffice/src/main/webapp/index.xhtml @@ -6,7 +6,7 @@ > <body> -<ui:decorate template="WEB-INF/templates/template.xhtml"> +<ui:decorate template="/WEB-INF/templates/template.xhtml"> <ui:define name="contenido"> <h1>Push</h1> <h:panelGroup id="messagePanel" layout="block"> diff --git a/backoffice/src/main/webapp/jsf/gestionhechos.xhtml b/backoffice/src/main/webapp/jsf/gestionhechos.xhtml index 2eff1a822edc4202497db4ac82f3f149606f233b..1780f2f7770da9e95f8262157d494ef7daf719cd 100644 --- a/backoffice/src/main/webapp/jsf/gestionhechos.xhtml +++ b/backoffice/src/main/webapp/jsf/gestionhechos.xhtml @@ -1,73 +1,49 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" - "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" - xmlns:h="http://xmlns.jcp.org/jsf/html" + xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" - xmlns:f="http://xmlns.jcp.org/jsf/core"> - -<h:head> - <style type="text/css"> - .ui-datatable .ui-datatable-header { - text-align: right !important; - } - - .ui-button-text-only .ui-button-text { - padding: 0.3em 0.4em; - } - - .ui-selectcheckboxmenu-panel .ui-selectcheckboxmenu-header .ui-chkbox { - visibility: hidden; - } - - .ui-filter-column .ui-column-customfilter .custom-filter { - width: 100%; - box-sizing: border-box; - } - - .year-spinner input { - width: 100%; - box-sizing: border-box; - } - - - </style> - -</h:head> - -<f:view> - <h:form > - <p:outputPanel style="font-size: 30px;text-align: center"> - <h:outputText value= "Bienvenido " /> - <h:outputText value= "#{userLoginView.user.mail}" /> - </p:outputPanel> - - <p:dataTable var="hecho" id="dataHecho" value="#{gestionHechos.filteredFacts}" widgetVar="NoticiasTable" - emptyMessage="Hecho no encontrada" filteredValue="#{gestionHechos.filteredFacts}"> - <f:facet name="header"> - <p:outputPanel> - <h:outputText value="Buscar en todos los campos:" /> - <p:inputText id="globalFilter" onkeyup="PF('NoticiasTable').filter()" style="width:150px" placeholder="Enter keyword"/> - </p:outputPanel> - </f:facet> - - <p:column filterBy="#{gestionHechos.filterTitulo}" headerText="Titulo" filterMatchMode="contains"> - <h:outputText value="#{hecho.title}" /> - </p:column> - <p:column filterBy="#{gestionHechos.filterDesc}" headerText="Descripción" filterMatchMode="contains"> - <h:outputText value="#{hecho.description}"/> - </p:column> - <p:column filterBy="#{gestionHechos.filterEstado}" headerText="Estados" filterMatchMode="exact"> - <f:facet name="filter"> - <p:selectOneMenu onchange="PF('NoticiasTable').filter()" styleClass="custom-filter"> - <f:selectItem itemLabel="Select One" itemValue="#{null}" noSelectionOption="true" /> - <f:selectItems value="#{gestionHechos.estados}" var="e" itemLabel="#{e.value}" - itemValue="#{e.value}"/> - </p:selectOneMenu> + xmlns:h="http://xmlns.jcp.org/jsf/html" + xmlns:f="http://xmlns.jcp.org/jsf/core" +> +<body> + +<ui:decorate template="/WEB-INF/templates/template.xhtml"> + <ui:define name="contenido"> + <h:form> + <p:outputPanel style="font-size: 30px;text-align: center"> + <h:outputText value="Bienvenido "/> + <h:outputText value="#{userLoginView.user.mail}"/> + </p:outputPanel> + + <p:dataTable var="hecho" id="dataHecho" value="#{gestionHechos.filteredFacts}" widgetVar="NoticiasTable" + emptyMessage="Hecho no encontrada" filteredValue="#{gestionHechos.filteredFacts}"> + <f:facet name="header"> + <p:outputPanel> + <h:outputText value="Buscar en todos los campos:"/> + <p:inputText id="globalFilter" onkeyup="PF('NoticiasTable').filter()" style="width:150px" + placeholder="Enter keyword"/> + </p:outputPanel> </f:facet> - <h:outputText value="#{hecho.actualState.value}"/> - </p:column> - </p:dataTable> - </h:form> -</f:view> -</html> + + <p:column filterBy="#{gestionHechos.filterTitulo}" headerText="Titulo" filterMatchMode="contains"> + <h:outputText value="#{hecho.title}"/> + </p:column> + <p:column filterBy="#{gestionHechos.filterDesc}" headerText="Descripción" filterMatchMode="contains"> + <h:outputText value="#{hecho.description}"/> + </p:column> + <p:column filterBy="#{gestionHechos.filterEstado}" headerText="Estados" filterMatchMode="exact"> + <f:facet name="filter"> + <p:selectOneMenu onchange="PF('NoticiasTable').filter()" styleClass="custom-filter"> + <f:selectItem itemLabel="Select One" itemValue="#{null}" noSelectionOption="true"/> + <f:selectItems value="#{gestionHechos.estados}" var="e" itemLabel="#{e.value}" + itemValue="#{e.value}"/> + </p:selectOneMenu> + </f:facet> + <h:outputText value="#{hecho.actualState.value}"/> + </p:column> + </p:dataTable> + </h:form> + </ui:define> +</ui:decorate> +</body> +</html> \ No newline at end of file diff --git a/backoffice/src/main/webapp/jsf/login.xhtml b/backoffice/src/main/webapp/login.xhtml similarity index 95% rename from backoffice/src/main/webapp/jsf/login.xhtml rename to backoffice/src/main/webapp/login.xhtml index 8ef52341a5dd28edb44e856a07819dea7eec2e1f..05a937d8811bcc4c81461bff40e50dfd0eb59179 100644 --- a/backoffice/src/main/webapp/jsf/login.xhtml +++ b/backoffice/src/main/webapp/login.xhtml @@ -26,7 +26,7 @@ update="form" async="true" process="@this"/> <p:commandButton value="Login" action="#{userLoginView.login()}" update="form"/> </f:facet> - <h:outputLabel value="#{userLoginView.token}"/> + <h:inputHidden id="token" value="#{sessionBean.token}"/> </h:panelGrid> </h:form> </ui:define> diff --git a/central-ejb/pom.xml b/central-ejb/pom.xml index 0af5d6ce6f11560a2458fac236a181397f4f34bb..dda23b8a223dedba05917c0f905e7eac5153cf42 100644 --- a/central-ejb/pom.xml +++ b/central-ejb/pom.xml @@ -60,6 +60,11 @@ <version>0.8.0</version> </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + </dependency> + <!-- Test scope dependencies --> <dependency> <groupId>junit</groupId> diff --git a/central-ejb/src/main/java/uy/edu/fing/tse/central/business/security/SecurityBean.java b/central-ejb/src/main/java/uy/edu/fing/tse/central/business/security/SecurityBean.java index 9130c9b904ebcb7385fed036e1b16b919135e377..37680eaf4d3dbc3e63914daee96fba39391b93f9 100644 --- a/central-ejb/src/main/java/uy/edu/fing/tse/central/business/security/SecurityBean.java +++ b/central-ejb/src/main/java/uy/edu/fing/tse/central/business/security/SecurityBean.java @@ -1,6 +1,8 @@ package uy.edu.fing.tse.central.business.security; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uy.edu.fing.tse.central.business.common.Secure; @@ -12,6 +14,8 @@ import uy.edu.fing.tse.dto.UserBO; import javax.annotation.PostConstruct; import javax.ejb.EJB; import javax.ejb.Stateless; +import java.util.Calendar; +import java.util.Date; import java.util.Objects; @Stateless @@ -49,9 +53,22 @@ public class SecurityBean implements SecurityLocal, SecurityRemote { final var thisPassword = Secure.encriptPBKDF(p.getPassword(), salt); if (Objects.equals(thisPassword, user.getPassword())) { - return thisPassword; + return createJWT(user.getMail(), user.getRole().getName()); } return null; } + private String createJWT(String role, String user) { + // prepare expiration date according to application properties + Date expDate = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(expDate); + + calendar.add(Calendar.MINUTE, 15); + expDate = calendar.getTime(); + + return Jwts.builder().setSubject(user).claim("role", role).setIssuedAt(new Date()).setExpiration(expDate) + .signWith(SignatureAlgorithm.HS256, "1q2w3e4r5t6y7u8i9o0p").compact(); + } + } diff --git a/pom.xml b/pom.xml index f76fe538d15a3892afb03507684b0cddd453fe3e..2d41da3d47117c26a6830fdeee16f339e7694248 100644 --- a/pom.xml +++ b/pom.xml @@ -205,6 +205,12 @@ <scope>provided</scope> </dependency> + <dependency> + <groupId>io.jsonwebtoken</groupId> + <artifactId>jjwt</artifactId> + <version>0.9.1</version> + </dependency> + <dependency> <groupId>org.jboss.spec.javax.faces</groupId> <artifactId>jboss-jsf-api_2.3_spec</artifactId>