Improvements to authorization

This commit is contained in:
tim holloway 2024-02-14 21:28:15 -05:00
parent ec17e0d72a
commit 21d4f58574
14 changed files with 112 additions and 105 deletions

View File

@ -12,27 +12,22 @@ import com.mousetech.gourmetj.persistence.model.Recipe;
import com.mousetech.gourmetj.persistence.service.RecipeService; import com.mousetech.gourmetj.persistence.service.RecipeService;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.event.AjaxBehaviorEvent; import jakarta.faces.event.AjaxBehaviorEvent;
import jakarta.faces.model.DataModel; import jakarta.faces.model.DataModel;
import jakarta.faces.model.ListDataModel; import jakarta.faces.model.ListDataModel;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
/** /**
* Main control panel backing bean. * Main control panel backing bean.
* *
* The rare and fabled RequestScope, which is otherwise
* useless 90% of the time. Here we maintain no session
* state. so we can better support the session timeout
* for editing functions.
*
* @author timh * @author timh
* @since Jun 28, 2012 * @since Jun 28, 2012
*/ */
@Named @Named
@RequestScoped @ViewScoped
public class AdminMainBean implements Serializable { public class AdminMainBean implements Serializable {
/** /**
@ -49,9 +44,6 @@ public class AdminMainBean implements Serializable {
private static final Logger log = private static final Logger log =
LoggerFactory.getLogger(AdminMainBean.class); LoggerFactory.getLogger(AdminMainBean.class);
/** Cookie delimiter */
private static final String CKDLM = ",";
/** /**
* Persistency service for Recipes. * Persistency service for Recipes.
*/ */
@ -289,4 +281,9 @@ public class AdminMainBean implements Serializable {
// items. // items.
return "recipeDetails?faces-redirect=true"; return "recipeDetails?faces-redirect=true";
} }
public String doLogout() {
JSFUtils.logout();
return null;
}
} }

View File

@ -164,5 +164,9 @@ public class JSFUtils {
log.warn("Session did not exist."); log.warn("Session did not exist.");
} }
}
public static HttpSession getSession(boolean create) {
return (HttpSession) getExternalContext().getSession(create);
} }
} }

View File

@ -14,12 +14,10 @@ import jakarta.faces.model.ListDataModel;
import jakarta.faces.view.ViewScoped; import jakarta.faces.view.ViewScoped;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import jakarta.inject.Named; import jakarta.inject.Named;
import jakarta.servlet.http.Part;
import jakarta.faces.event.AjaxBehaviorEvent; import jakarta.faces.event.AjaxBehaviorEvent;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.primefaces.event.FileUploadEvent; import org.primefaces.event.FileUploadEvent;
import org.primefaces.model.file.UploadedFile;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -980,7 +978,7 @@ public class RecipeDetailBean implements Serializable {
public String editDescription() { public String editDescription() {
this.setDetailTab(0); this.setDetailTab(0);
return "detailEdit?faces-redirect=true"; return "detailEdit.xhtml?faces-redirect=true";
} }
public String editIngredients() { public String editIngredients() {

View File

@ -21,10 +21,11 @@ import org.springframework.http.HttpStatus;
"com.mousetech.gourmetj.persistence.model" }) "com.mousetech.gourmetj.persistence.model" })
public class SpringPrimeFacesApplication { public class SpringPrimeFacesApplication {
final String homePage = "/main.jsf?viewExpired=true";
final String errorPage = "/error/error.html"; final String errorPage = "/error/error.html";
final String error404Page = "/error/error404.html"; final String error404Page = "/error/error404.jsp";
final String error400Page = "/error/error400.jsp"; final String error400Page = "/error/error400.jsp";
final String expiredPage = "/main.xhtml"; final String expiredPage = "/error/viewExpired.xhtml";
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SpringPrimeFacesApplication.class, SpringApplication.run(SpringPrimeFacesApplication.class,

View File

@ -20,6 +20,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import jakarta.servlet.DispatcherType; import jakarta.servlet.DispatcherType;
@ -99,24 +100,24 @@ public class SpringSecurityConfig {
return ocreds; return ocreds;
} }
@Bean @Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http http.csrf(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable) .cors(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable) .formLogin(login -> login.loginPage("/login.jsf")
.formLogin(formLogin -> .permitAll()
formLogin .failureUrl("/login.jsf?error=true"))
.loginPage("/login.xhtml") .logout(logout -> logout
.permitAll()) .logoutSuccessUrl("/login.jsf"))
.authorizeHttpRequests((authorize)-> authorize .httpBasic(Customizer.withDefaults())
.dispatcherTypeMatchers(DispatcherType.FORWARD, DispatcherType.ERROR).permitAll() .authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated() .anyRequest().authenticated());
);
return http.build();
}
return http.build();
}
/** /**
* Replaces old antMatchers for determining secured URLs. * Replaces old antMatchers for determining secured URLs.
* @return customizer * @return customizer
@ -124,14 +125,19 @@ public class SpringSecurityConfig {
@Bean @Bean
public WebSecurityCustomizer webSecurityCustomizer() { public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers( return (web) -> web.ignoring().requestMatchers(
"/jakarta.faces.resource/**", "/jakarta.faces.resource/**",
"/index.xhtml",
"/", "/",
"/index.jsf", "/index.html",
"/login", // "/login",
// "/login.jsf", // Leave them for the authenticator!
// "/login.xhtml",
"/main.jsf", "/main.jsf",
"/main.xhtml",
"/img/**", "/img/**",
"/error/**",
"/RES_NOT_FOUND",
"/recipeDetails.jsf", "/recipeDetails.jsf",
"/recipeDetails.xhtml",
"/shoppingList.jsf", "/shoppingList.jsf",
"/recipePrint.jsf"); "/recipePrint.jsf");
} }

View File

@ -5,10 +5,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import jakarta.enterprise.context.SessionScoped; import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.model.SelectItem;
import jakarta.inject.Named; import jakarta.inject.Named;
import org.primefaces.PrimeFaces;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;

View File

@ -25,7 +25,7 @@
<navigation-case> <navigation-case>
<description>Go Home</description> <description>Go Home</description>
<from-outcome>home</from-outcome> <from-outcome>home</from-outcome>
<to-view-id>/main</to-view-id> <to-view-id>/main.xhtml?faces-redirect=true</to-view-id>
<redirect /> <redirect />
</navigation-case> </navigation-case>
</navigation-rule> </navigation-rule>

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<html>
<head>
<title>ERROR - Page Not Found</title>
</head>
<body>
<h1>Page Not Found</h1>
<p>This URL is invalid.</p>
<p><a href="/main.jsf">Return to Main Page</a></p>
</body>
</html>

View File

@ -0,0 +1,12 @@
<%@ page language="java" contentType="text/html; charset=US-ASCII"
pageEncoding="US-ASCII" isErrorPage="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ERROR - Page Not Found</title>
</head>
<body>
<h1>Page Not Found</h1>
<p><a href="/main.jsf">Return to Main Page</a></p>
</body>
</html>

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>ERROR - Page Expired</title>
</head>
<body>
<h1>Page Expired</h1>
<p>The page state could not be restored because it was
left idle too long.</p>
<p>
<a href="/main.jsf">Return to Main Page</a>
</p>
</body>
</html>

View File

@ -1,13 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" <html>
xmlns:h="http://xmlns.jcp.org/jsf/html" <head><title>Gourmet Recipe Manager</title><head>
xmlns:f="http://xmlns.jcp.org/jsf/core" <body>
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
>
<h:head>Gourmet Recipe Manager</h:head>
<h:body>
<h1>Gourmet Recipe Manager</h1> <h1>Gourmet Recipe Manager</h1>
<p>This is an implementation of Thomas Hinkle's <p>This is an implementation of Thomas Hinkle's
Gourmet Recipe Manager, originally a desktop Gourmet Recipe Manager, originally a desktop
@ -20,5 +15,5 @@
<p>This is an open-source application under the <p>This is an open-source application under the
Common Development and Distribution License (CDDL). Common Development and Distribution License (CDDL).
</p> </p>
</h:body> <body>
</html> </html>

View File

@ -1,31 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?> <?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" <html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html" xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui" xmlns:p="http://primefaces.org/ui"
xmlns:pe="http://primefaces.org/ui/extensions"> xmlns:pe="http://primefaces.org/ui/extensions"
>
<h:head> <h:head>
<title>Login</title> <title>Login</title>
<h:outputStylesheet name="/css/login.css" />
</h:head> </h:head>
<h:body> <h:body>
<h:form prependId="false"> <h:form prependId="false" style="width:100%">
<p:panelGrid columns="3" style="width:100%"
<p:panelGrid columns="1" styleClass="ui-fluid center ui-noborder"> styleClass="ui-fluid center ui-noborder"
<h2>Please login</h2> >
<h:outputText style="width:33%;" value=" " />
<p:outputLabel value="Login failed!" styleClass="red" <p:panelGrid columns="1" id="grid1">
rendered="${!empty param['error']}" /> <h:outputText>Please login</h:outputText>
<p:outputLabel value="Login failed!"
<p:inputText id="username" placeholder="User name" /> styleClass="red"
<p:password id="password" placeholder="Password" /> rendered="${!empty param['error']}"
/>
<p:commandButton value="Login" ajax="false" /> <p:outputLabel for="username">User ID</p:outputLabel>
</p:panelGrid> <p:inputText id="username"
placeholder="User name"
</h:form> />
<p:outputLabel for="password">Password</p:outputLabel>
<p:password id="password" placeholder="Password" />
<p:commandButton value="Login" ajax="false" />
</p:panelGrid>
<h:outputText style="width:33%;" value=" " />
</p:panelGrid>
</h:form>
</h:body> </h:body>
</html> </html>

View File

@ -20,13 +20,7 @@
listener="#{adminMainBean.ajaxUpdateList}" listener="#{adminMainBean.ajaxUpdateList}"
/> />
</p:autoComplete> </p:autoComplete>
<p:defaultCommand target="find" /> <p:outputLabel for="@next" value=" In " />
<p:commandButton id="find" value="Find"
icon="ui-icon-search"
action="#{adminMainBean.doFind}"
update=":form2:table1"
/>
<p:outputLabel for="@next" value="Search for " />
<p:selectOneMenu id="ctlSearchType" <p:selectOneMenu id="ctlSearchType"
value="#{cookieBean.searchType}" value="#{cookieBean.searchType}"
> >
@ -37,6 +31,12 @@
listener="#{adminMainBean.resetSuggestions}" listener="#{adminMainBean.resetSuggestions}"
/> />
</p:selectOneMenu> </p:selectOneMenu>
<p:defaultCommand target="find" />
<p:commandButton id="find" value="Find"
icon="ui-icon-search"
action="#{adminMainBean.doFind}"
update=":form2:table1"
/>
<p:commandButton id="ctlClear" value="Clear" <p:commandButton id="ctlClear" value="Clear"
icon="ui-icon-close" icon="ui-icon-close"
update="@form:searchFor :form2:table1" update="@form:searchFor :form2:table1"
@ -55,6 +55,9 @@
<h:outputLabel for="slistSize" <h:outputLabel for="slistSize"
value=" Recipes in Shopping List" value=" Recipes in Shopping List"
/> />
<p:commandButton id="logout" value="Logout"
action="#{adminMainBean.doLogout}"
/>
</div> </div>
</h:form> </h:form>
<h:form id="form2"> <h:form id="form2">

View File

@ -21,10 +21,13 @@ spring:
ddl-auto: none ddl-auto: none
database-platform: org.hibernate.dialect.MySQLDialect database-platform: org.hibernate.dialect.MySQLDialect
# Tracking-modes prevent URL rewrite jsessionid on Primecases
# resources. Which causes "400" errors on initial main.jsf fetch.
server: server:
servlet: servlet:
session: session:
timeout: '30m' timeout: '30m'
tracking-modes: 'cookie'
# Theme here overrides joinfaces theme # Theme here overrides joinfaces theme
# context-parameters: # context-parameters:
# primefaces: # primefaces:
@ -38,3 +41,14 @@ gourmet:
joinfaces: joinfaces:
primefaces: primefaces:
theme: bluesky theme: bluesky
faces:
project-stage: Production
facelets-libraries: /tags/tags.taglib.xml
#logging:
# level:
# org.springframework.security: TRACE
# org.apache.catalina: TRACE
# jakarta.faces: TRACE
# com.sun.faces: TRACE
# jakarta.servlet: TRACE