/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security.password;
import java.io.IOException;
import java.security.Security;
import java.util.logging.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.geoserver.security.GeoServerSecurityManager;
import org.geoserver.security.GeoServerUserGroupService;
import org.geotools.util.logging.Logging;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.encoding.PasswordEncoder;
/**
* Abstract base implementation, delegating the encoding
* to third party encoders implementing {@link PasswordEncoder}
*
* @author christian
*
*/
public abstract class AbstractGeoserverPasswordEncoder implements GeoServerPasswordEncoder {
static protected Logger LOGGER = Logging.getLogger("org.geoserver.security");
protected volatile PasswordEncoder stringEncoder;
protected volatile CharArrayPasswordEncoder charEncoder;
protected String name;
private boolean availableWithoutStrongCryptogaphy;
private boolean reversible = true;
private String prefix;
static {
Security.addProvider(new BouncyCastleProvider());
}
public String getName() {
return name;
}
public void setBeanName(String beanName) {
this.name = beanName;
}
/**
* Does nothing, subclases may override.
*/
public void initialize(GeoServerSecurityManager securityManager) throws IOException {
}
/**
* Does nothing, subclases may override.
*/
public void initializeFor(GeoServerUserGroupService service) throws IOException {
}
public AbstractGeoserverPasswordEncoder() {
setAvailableWithoutStrongCryptogaphy(true);
}
protected PasswordEncoder getStringEncoder() {
if (stringEncoder == null) {
synchronized (this) {
if (stringEncoder == null) {
stringEncoder = createStringEncoder();
}
}
}
return stringEncoder;
}
/**
* Creates the encoder instance used when source is a string.
*/
protected abstract PasswordEncoder createStringEncoder();
protected CharArrayPasswordEncoder getCharEncoder() {
if (charEncoder == null) {
synchronized (this) {
if (charEncoder == null) {
charEncoder = createCharEncoder();
}
}
}
return charEncoder;
}
/**
* Creates the encoder instance used when source is a char array.
*/
protected abstract CharArrayPasswordEncoder createCharEncoder();
/**
* @return the concrete {@link PasswordEncoder} object
*/
protected final PasswordEncoder getActualEncoder() {
return null;
}
@Override
public String encodePassword(String rawPass, Object salt) throws DataAccessException {
return doEncodePassword(getStringEncoder().encodePassword(rawPass, salt));
}
@Override
public String encodePassword(char[] rawPass, Object salt)
throws DataAccessException {
return doEncodePassword(getCharEncoder().encodePassword(rawPass, salt));
}
String doEncodePassword(String encPass) {
if (encPass == null) {
return encPass;
}
StringBuffer buff = initPasswordBuffer();
buff.append(encPass);
return buff.toString();
}
StringBuffer initPasswordBuffer() {
StringBuffer buff = new StringBuffer();
if (getPrefix() != null) {
buff.append(getPrefix()).append(GeoServerPasswordEncoder.PREFIX_DELIMTER);
}
return buff;
}
@Override
public boolean isPasswordValid(String encPass, String rawPass, Object salt)
throws DataAccessException {
if (encPass==null) return false;
return getStringEncoder().isPasswordValid(stripPrefix(encPass), rawPass, salt);
}
@Override
public boolean isPasswordValid(String encPass, char[] rawPass, Object salt) {
if (encPass==null) return false;
return getCharEncoder().isPasswordValid(stripPrefix(encPass), rawPass, salt);
}
String stripPrefix(String encPass) {
return getPrefix() != null ? removePrefix(encPass) : encPass;
}
protected String removePrefix(String encPass) {
return encPass.replaceFirst(getPrefix()+GeoServerPasswordEncoder.PREFIX_DELIMTER, "");
}
@Override
public abstract PasswordEncodingType getEncodingType();
/**
* @param encPass
* @return true if this encoder has encoded encPass
*/
public boolean isResponsibleForEncoding(String encPass) {
if (encPass==null) return false;
return encPass.startsWith(getPrefix()+GeoServerPasswordEncoder.PREFIX_DELIMTER);
}
public String decode(String encPass) throws UnsupportedOperationException {
throw new UnsupportedOperationException("decoding passwords not supported");
}
@Override
public char[] decodeToCharArray(String encPass)
throws UnsupportedOperationException {
throw new UnsupportedOperationException("decoding passwords not supported");
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public boolean isAvailableWithoutStrongCryptogaphy() {
return availableWithoutStrongCryptogaphy;
}
public void setAvailableWithoutStrongCryptogaphy(boolean availableWithoutStrongCryptogaphy) {
this.availableWithoutStrongCryptogaphy = availableWithoutStrongCryptogaphy;
}
public boolean isReversible() {
return reversible;
}
public void setReversible(boolean reversible) {
this.reversible = reversible;
}
/**
* Interface for password encoding when source password is specified as char array.
*/
protected static interface CharArrayPasswordEncoder {
String encodePassword(char[] rawPass, Object salt);
boolean isPasswordValid(String encPass, char[] rawPass, Object salt);
}
}