Support for password file.

This commit is contained in:
Tim Holloway 2022-01-18 09:46:28 -05:00
parent f3c28258d1
commit fa7e832996
4 changed files with 88 additions and 4 deletions

View File

@ -49,6 +49,13 @@ You must have a recipe database file (see below) to store the
recipes in. By default, it will be looked for in your recipes in. By default, it will be looked for in your
home directory. home directory.
As of the 0.1.4 release, the parts of the application that can
alter the database are now password-protected. You will need
a ``.gourmetpw`` file to contain your userid/password
definitions. By default it should be in the same directory
that you are running the application from. A sample password file
is included in this project.
To actually access the application, open your web browser To actually access the application, open your web browser
to ``http://localhost:8080`` to ``http://localhost:8080``

View File

@ -17,3 +17,6 @@ spring.datasource.driverClassName=org.sqlite.JDBC
#spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect #spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.database-platform=org.sqlite.hibernate.dialect.SQLiteDialect spring.jpa.database-platform=org.sqlite.hibernate.dialect.SQLiteDialect
#spring.jpa.show-sql: true #spring.jpa.show-sql: true
# My special properties
gourmet.password.file=.gourmetpw

15
gourmetpw.sample Normal file
View File

@ -0,0 +1,15 @@
# This is a sample password file for the Gourmetj webapp.
# The actual file should be named ".gourmetpw" and located
# in the same directory that you run the application from.
#
# Blank lines and lines beginning with "#" are ignored.
# Password lines look like this (remove leading "#")
#
# userid,password,role[,role,...]
#
# like so:
#
# john.smith,secretpassword,USER
#
# Where "role" is a security role. ADMIN is also allowed.
#

View File

@ -1,8 +1,19 @@
package com.mousetech.gourmetj; package com.mousetech.gourmetj;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberInputStream;
import java.io.LineNumberReader;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.authentication.configurers.provisioning.UserDetailsManagerConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@ -12,6 +23,11 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
public class SpringSecurityConfig public class SpringSecurityConfig
extends WebSecurityConfigurerAdapter { extends WebSecurityConfigurerAdapter {
/* Logger */
private static final Logger log =
LoggerFactory.getLogger(SpringSecurityConfig.class);
@Override @Override
protected void configure(HttpSecurity http) protected void configure(HttpSecurity http)
throws Exception { throws Exception {
@ -32,12 +48,55 @@ public class SpringSecurityConfig
http.csrf().disable(); http.csrf().disable();
} }
@Value("${gourmet.password.file}")
private String passwordFile;
@Autowired @Autowired
public void configureGlobal( public void configureGlobal(
AuthenticationManagerBuilder auth) throws Exception { AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("tim.holloway")
.password("{noop}secret").roles("ADMIN").and() File pwFile = new File(passwordFile);
.withUser("jane.doe").password("{noop}5678") if (!pwFile.canRead()) {
.roles("USER"); String msg =
"Password file '" + pwFile.getAbsolutePath()
+ "' could not be found or read.";
log.error(msg);
throw new RuntimeException(msg);
}
LineNumberReader rdr =
new LineNumberReader(new FileReader(pwFile));
String pwLine;
InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> authenticator =
auth.inMemoryAuthentication();
while ((pwLine = rdr.readLine()) != null) {
pwLine = pwLine.trim();
if (( pwLine.length() == 0) || (pwLine.charAt(0) == '#')) {
continue;
}
String[] creds = parseCreds(pwLine);
UserDetailsManagerConfigurer<AuthenticationManagerBuilder, InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder>>.UserDetailsBuilder bar =
authenticator.withUser(creds[0])
.password("{noop}"+creds[1]);
int credlen = creds.length;
for (int i = 2; i < credlen; i++) {
bar.roles(creds[i]);
}
}
rdr.close();
}
/**
* Parse CSV credential/roles line. Element 1 is userid,
* element 2 is password, following element(s) are role(s)
*
* @param pwLine
* @return Credentials array following CSV values, trimmed
*/
private String[] parseCreds(String pwLine) {
String[] creds = pwLine.split(",");
String[] ocreds = Arrays.stream(creds).map(e -> e.trim())
.toArray(String[]::new);
return ocreds;
} }
} }