Skip to content

Commit

Permalink
Merge pull request #66 from mbien/spring_renovations
Browse files Browse the repository at this point in the history
Spring Renovations
  • Loading branch information
snoopdave authored Oct 11, 2020
2 parents c18b903 + d53b0c0 commit fe697f4
Show file tree
Hide file tree
Showing 14 changed files with 225 additions and 130 deletions.
14 changes: 12 additions & 2 deletions app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ limitations under the License.
<maven-antrun.version>1.0b3</maven-antrun.version>
<rome.version>1.13.1</rome.version>
<slf4j.version>1.7.26</slf4j.version>
<spring.version>4.1.4.RELEASE</spring.version>
<spring.security.version>3.2.5.RELEASE</spring.security.version>
<spring.version>5.2.7.RELEASE</spring.version>
<spring.security.version>5.3.3.RELEASE</spring.security.version>
<struts.version>2.5.22</struts.version>
<velocity.version>1.7</velocity.version>
<webjars.version>1.5</webjars.version>
Expand Down Expand Up @@ -215,6 +215,16 @@ limitations under the License.
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>${struts.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
Expand Down
16 changes: 5 additions & 11 deletions app/src/main/java/org/apache/roller/weblogger/pojos/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.util.UUIDGenerator;
import org.apache.roller.weblogger.business.WebloggerFactory;
import org.apache.roller.weblogger.util.Utilities;
import org.apache.roller.weblogger.ui.core.RollerContext;
import org.springframework.security.crypto.password.PasswordEncoder;


/**
Expand Down Expand Up @@ -115,15 +115,9 @@ public void setPassword( String password ) {
*
* @param newPassword The new password to be set.
*/
public void resetPassword(String newPassword) throws WebloggerException {

String encrypt = WebloggerConfig.getProperty("passwds.encryption.enabled");
String algorithm = WebloggerConfig.getProperty("passwds.encryption.algorithm");
if (Boolean.valueOf(encrypt)) {
setPassword(Utilities.encodePassword(newPassword, algorithm));
} else {
setPassword(newPassword);
}
public void resetPassword(String newPassword) {
PasswordEncoder encoder = RollerContext.getPasswordEncoder();
setPassword(encoder.encode(newPassword));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
Expand All @@ -30,9 +32,6 @@
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.authentication.encoding.ShaPasswordEncoder;
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.apache.commons.logging.Log;
Expand All @@ -50,6 +49,10 @@
import org.apache.velocity.runtime.RuntimeSingleton;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.WebApplicationContextUtils;

Expand All @@ -63,6 +66,7 @@ public class RollerContext extends ContextLoaderListener
private static Log log = LogFactory.getLog(RollerContext.class);

private static ServletContext servletContext = null;
private static DelegatingPasswordEncoder encoder;


public RollerContext() {
Expand All @@ -89,6 +93,9 @@ public static ServletContext getServletContext() {
return servletContext;
}

public static PasswordEncoder getPasswordEncoder() {
return encoder;
}

/**
* Responds to app-init event and triggers startup procedures.
Expand Down Expand Up @@ -251,27 +258,14 @@ protected void initializeSecurityFeatures(ServletContext context) {
}
}

String encryptPasswords = WebloggerConfig.getProperty("passwds.encryption.enabled");
boolean doEncrypt = Boolean.valueOf(encryptPasswords);
encoder = createPasswordEncoder();

String daoBeanName = "org.springframework.security.authentication.dao.DaoAuthenticationProvider#0";

// for LDAP-only authentication, no daoBeanName (i.e., UserDetailsService) may be provided in security.xml.
if (doEncrypt && ctx.containsBean(daoBeanName)) {
if (ctx.containsBean(daoBeanName)) {
DaoAuthenticationProvider provider = (DaoAuthenticationProvider) ctx.getBean(daoBeanName);
String algorithm = WebloggerConfig.getProperty("passwds.encryption.algorithm");
PasswordEncoder encoder = null;
if (algorithm.equalsIgnoreCase("SHA")) {
encoder = new ShaPasswordEncoder();
} else if (algorithm.equalsIgnoreCase("MD5")) {
encoder = new Md5PasswordEncoder();
} else {
log.error("Encryption algorithm '" + algorithm + "' not supported, disabling encryption.");
}
if (encoder != null) {
provider.setPasswordEncoder(encoder);
log.info("Password Encryption Algorithm set to '" + algorithm + "'");
}
provider.setPasswordEncoder(encoder);
}

if (WebloggerConfig.getBooleanProperty("securelogin.enabled")) {
Expand All @@ -281,6 +275,58 @@ protected void initializeSecurityFeatures(ServletContext context) {
}
}

@SuppressWarnings("deprecation")
private DelegatingPasswordEncoder createPasswordEncoder() {

Map<String, PasswordEncoder> encoders = new HashMap<>();

// outdated digest encoder used for lazy upgrades from pws encoded by old roller versions.
String migrateFrom = WebloggerConfig.getProperty("passwds.encryption.lazyUpgradeFrom");

if(migrateFrom == null || migrateFrom.isEmpty()) {
log.debug("lazy pw upgrade disabled");
} else if (migrateFrom.equals("plaintext")) {
encoders.put(null, org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
} else if (migrateFrom.equals("MD5")) {
encoders.put(null, new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
} else if (migrateFrom.equals("SHA")) {
encoders.put(null, new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
} else {
throw new RuntimeException("passwds.encryption.lazyUpgradeFrom="+migrateFrom+" is no valid encoding to upgrade from.");
}

// supported encoders
encoders.put("bcrypt", new BCryptPasswordEncoder());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
// requires bouncy castle impl
// encoders.put("scrypt", new SCryptPasswordEncoder());
// encoders.put("argon2", new Argon2PasswordEncoder());

// just for testing
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());

String algorithm = WebloggerConfig.getProperty("passwds.encryption.algorithm");

if (WebloggerConfig.getBooleanProperty("passwds.encryption.enabled")) {

if ("SHA".equals(algorithm) || "MD5".equals(algorithm)) {
throw new RuntimeException("passwds.encryption.algorithm="+algorithm+" is outdated,"
+ " please set passwds.encryption.algorithm to 'bcrypt' for automatic lazy upgrade.");
}

if (!encoders.containsKey(algorithm)) {
throw new RuntimeException("passwds.encryption.algorithm="+algorithm+" is not supported.");
}
} else {
log.warn("New passwords are stored in plain text!");
algorithm = "noop";
}

log.info("Password Encryption Algorithm set to '" + algorithm + "'");

return new DelegatingPasswordEncoder(algorithm, encoders);
}


/**
* Flush user from any caches maintained by security system.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/

package org.apache.roller.weblogger.ui.core.filters;

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.firewall.RequestRejectedException;

/**
* <p>The spring default firewall ({@link org.springframework.security.web.firewall.StrictHttpFirewall})
* is throwing exceptions if it decides to block a request. For example double slashes (//) in the request are
* interpreted as non-normalized URL and rejected by throwing RequestRejectedExceptions.
* Those exceptions are caught by the server and cause 500 errors which isn't very nice behavior.</p>
*
* <p>The most straightforward way to handle this seems to be a servlet filter.</p>
*
* @see org.springframework.security.web.firewall.StrictHttpFirewall
*/
public class SpringFirewallExceptionFilter implements Filter {

private final static Log log = LogFactory.getLog(SpringFirewallExceptionFilter.class);

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (RequestRejectedException ex) {

HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;

// url seems to be dangerous -> log & 404
log.warn("request rejected: " + req.getRequestURL() + " cause: " + ex.getMessage());
resp.sendError(HttpServletResponse.SC_NOT_FOUND);

}
}

@Override
public void destroy() {}

@Override
public void init(FilterConfig filterConfig) throws ServletException {}


}
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,20 @@


public class RollerRememberMeAuthenticationProvider extends RememberMeAuthenticationProvider {
private static final Log log = LogFactory.getLog(RollerRememberMeServices.class);
private static final Log log = LogFactory.getLog(RollerRememberMeAuthenticationProvider.class);


public RollerRememberMeAuthenticationProvider() {

super(WebloggerConfig.getProperty("rememberme.key", "springRocks"));

log.debug("initializing: RollerRememberMeAuthenticationProvider");

String key = WebloggerConfig.getProperty("rememberme.key", "springRocks");

if ("springRocks".equals(key)) {
if (WebloggerConfig.getBooleanProperty("rememberme.enabled") && "springRocks".equals(getKey())) {
throw new RuntimeException(
"If remember-me is to be enabled, rememberme.key must be specified in the roller " +
"properties file. Make sure it is a secret and make sure it is NOT springRocks");
}
setKey(key);

log.debug("initialized: RollerRememberMeAuthenticationProvider with key: " + getKey());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.config.AuthMethod;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices;

Expand All @@ -33,17 +34,17 @@ public class RollerRememberMeServices extends TokenBasedRememberMeServices {
private static final Log log = LogFactory.getLog(RollerRememberMeServices.class);


public RollerRememberMeServices() {
public RollerRememberMeServices(UserDetailsService userDetailsService) {

super(WebloggerConfig.getProperty("rememberme.key", "springRocks"), userDetailsService);

log.debug("initializing: RollerRememberMeServices");

String key = WebloggerConfig.getProperty("rememberme.key", "springRocks");

if ("springRocks".equals(key)) {
if (WebloggerConfig.getBooleanProperty("rememberme.enabled") && "springRocks".equals(getKey())) {
throw new RuntimeException(
"If remember-me is to be enabled, rememberme.key must be specified in the roller " +
"properties file. Make sure it is a secret and make sure it is NOT springRocks");
}
setKey(key);

log.debug("initialized: RollerRememberMeServices with key");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import org.apache.roller.weblogger.pojos.WeblogPermission;
import org.apache.roller.weblogger.ui.struts2.core.Register;
import org.apache.roller.weblogger.ui.struts2.util.UIAction;
import org.apache.struts2.convention.annotation.AllowedMethods;
import org.apache.struts2.interceptor.validation.SkipValidation;


Expand Down Expand Up @@ -159,21 +158,13 @@ public String save() {
// User.password does not allow null, so generate one
if (authMethod.equals(AuthMethod.OPENID) ||
(authMethod.equals(AuthMethod.DB_OPENID) && !StringUtils.isEmpty(bean.getOpenIdUrl()))) {
try {
String randomString = RandomStringUtils.randomAlphanumeric(255);
user.resetPassword(randomString);
} catch (WebloggerException e) {
addMessage("yourProfile.passwordResetError");
}
String randomString = RandomStringUtils.randomAlphanumeric(255);
user.resetPassword(randomString);
}

// reset password if set
if (!StringUtils.isEmpty(getBean().getPassword())) {
try {
user.resetPassword(getBean().getPassword());
} catch (WebloggerException e) {
addMessage("yourProfile.passwordResetError");
}
user.resetPassword(getBean().getPassword());
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@
import org.apache.roller.weblogger.business.startup.WebloggerStartup;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.weblogger.ui.struts2.util.UIAction;
import org.apache.struts2.convention.annotation.AllowedMethods;
import org.springframework.beans.factory.access.BootstrapException;
import org.springframework.beans.FatalBeanException;


/**
Expand Down Expand Up @@ -173,8 +172,8 @@ public String bootstrap() {
log.info("EXITING - Bootstrap successful, forwarding to Roller");
return SUCCESS;

} catch (BootstrapException ex) {
log.error("BootstrapException", ex);
} catch (FatalBeanException ex) {
log.error("FatalBeanException", ex);
rootCauseException = ex;
} catch (WebloggerException ex) {
log.error("WebloggerException", ex);
Expand Down
Loading

0 comments on commit fe697f4

Please sign in to comment.