/* (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.jdbc;
import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Logger;
import org.geoserver.platform.resource.Resource;
import org.geoserver.security.GeoServerUserGroupService;
import org.geoserver.security.GeoServerUserGroupStore;
import org.geoserver.security.KeyStoreProvider;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.config.SecurityUserGroupServiceConfig;
import org.geoserver.security.event.UserGroupLoadedEvent;
import org.geoserver.security.event.UserGroupLoadedListener;
import org.geoserver.security.impl.GeoServerUser;
import org.geoserver.security.impl.GeoServerUserGroup;
import org.geoserver.security.impl.RoleCalculator;
import org.geoserver.security.impl.Util;
import org.geoserver.security.jdbc.config.JDBCSecurityServiceConfig;
import org.geoserver.security.jdbc.config.JDBCUserGroupServiceConfig;
import org.geoserver.security.password.GeoServerPasswordEncoder;
import org.geoserver.security.password.PasswordEncodingType;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;
/**
* JDBC implementation of {@link GeoServerUserGroupService}
*
* @author christian
*
*/
public class JDBCUserGroupService extends AbstractJDBCService implements GeoServerUserGroupService {
final static String DEFAULT_DML_FILE="usersdml.xml";
final static String DEFAULT_DDL_FILE="usersddl.xml";
protected SortedSet<GeoServerUser> emptyUsers;
protected SortedSet<GeoServerUserGroup> emptyGroups;
/** logger */
static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.security.jdbc");
protected Set<UserGroupLoadedListener> listeners =
Collections.synchronizedSet(new HashSet<UserGroupLoadedListener>());
protected String passwordEncoderName,passwordValidatorName;
public JDBCUserGroupService() throws IOException{
emptyUsers=Collections.unmodifiableSortedSet(new TreeSet<GeoServerUser>());
emptyGroups=Collections.unmodifiableSortedSet(new TreeSet<GeoServerUserGroup>());
}
@Override
public String getPasswordEncoderName() {
return passwordEncoderName;
}
@Override
public String getPasswordValidatorName() {
return passwordValidatorName;
}
@Override
public boolean canCreateStore() {
return true;
}
@Override
public GeoServerUserGroupStore createStore() throws IOException {
JDBCUserGroupStore store = new JDBCUserGroupStore();
store.initializeFromService(this);
return store;
}
/** Uses {@link #initializeDSFromConfig(SecurityNamedServiceConfig)} and
* {@link #checkORCreateJDBCPropertyFile(String, File, String)} for initializing
* @see org.geoserver.security.GeoServerUserGroupService#initializeFromConfig(org.geoserver.security.config.SecurityNamedServiceConfig)
*/
@Override
public void initializeFromConfig(SecurityNamedServiceConfig config) throws IOException {
this.name=config.getName();
passwordEncoderName=((SecurityUserGroupServiceConfig)config).getPasswordEncoderName();
passwordValidatorName=((SecurityUserGroupServiceConfig)config).getPasswordPolicyName();
initializeDSFromConfig(config);
if (config instanceof JDBCUserGroupServiceConfig) {
JDBCUserGroupServiceConfig jdbcConfig =
(JDBCUserGroupServiceConfig) config;
String fileNameDML =jdbcConfig.getPropertyFileNameDML();
Resource file = checkORCreateJDBCPropertyFile(fileNameDML, getConfigRoot(), DEFAULT_DML_FILE);
dmlProps = Util.loadUniversal(file.in());
String fileNameDDL =jdbcConfig.getPropertyFileNameDDL();
if (fileNameDDL!=null && fileNameDDL.length()> 0 ) {
file = checkORCreateJDBCPropertyFile(fileNameDDL, getConfigRoot(), DEFAULT_DDL_FILE);
ddlProps = Util.loadUniversal(file.in());
createTablesIfRequired((JDBCSecurityServiceConfig)config);
}
GeoServerPasswordEncoder enc = getSecurityManager().loadPasswordEncoder(passwordEncoderName);
if (enc.getEncodingType()==PasswordEncodingType.ENCRYPT) {
KeyStoreProvider prov = getSecurityManager().getKeyStoreProvider();
String alias = prov.aliasForGroupService(name);
if (prov.containsAlias(alias)==false) {
prov.setUserGroupKey(name,
getSecurityManager().getRandomPassworddProvider().getRandomPasswordWithDefaultLength());
prov.storeKeyStore();
}
}
enc.initializeFor(this);
passwordValidatorName=jdbcConfig.getPasswordPolicyName();
}
}
/**
* @see org.geoserver.security.jdbc.AbstractJDBCService#getOrderedNamesForCreate()
*/
protected String[] getOrderedNamesForCreate() {
return new String[] {
"users.create","userprops.create","groups.create","groupmembers.create",
"groupmembers.indexcreate","userprops.indexcreate1","userprops.indexcreate2"
};
}
/**
* @see org.geoserver.security.jdbc.AbstractJDBCService#getOrderedNamesForDrop()
*/
protected String[] getOrderedNamesForDrop() {
return new String[] {
"groupmembers.drop","groups.drop","userprops.drop","users.drop"
};
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getUserByUsername(java.lang.String)
*/
public GeoServerUser getUserByUsername(String username) throws IOException {
Connection con=null;
PreparedStatement ps = null,ps2=null;
ResultSet rs = null,rs2=null;
GeoServerUser u = null;
try {
con = getConnection();
ps = getDMLStatement("users.keyed",con);
ps.setString(1, username);
rs = ps.executeQuery();
if (rs.next()) {
String password = rs.getString(1);
String enabledString = rs.getString(2);
boolean isEnabled = convertFromString(enabledString);
u = createUserObject(username,password, isEnabled);
ps2 = getDMLStatement("userprops.selectForUser",con);
ps2.setString(1, username);
rs2 = ps2.executeQuery();
while (rs2.next()) {
String propName = rs2.getString(1);
Object propValue = rs2.getObject(2);
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(null, ps2, rs2);
closeFinally(con, ps, rs);
}
return u;
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getGroupByGroupname(java.lang.String)
*/
public GeoServerUserGroup getGroupByGroupname(String groupname) throws IOException {
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
GeoServerUserGroup g =null;
try {
con = getConnection();
ps = getDMLStatement("groups.keyed",con);
ps.setString(1, groupname);
rs = ps.executeQuery();
if (rs.next()) {
String enabledString = rs.getString(1);
boolean isEnabled= convertFromString(enabledString);
g = createGroupObject(groupname, isEnabled);
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return g;
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getUsers()
*/
public SortedSet<GeoServerUser> getUsers() throws IOException{
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Map<String,GeoServerUser> map = new HashMap<String,GeoServerUser>();
try {
con = getConnection();
ps = getDMLStatement("users.all",con);
rs = ps.executeQuery();
while (rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
String enabledString = rs.getString(3);
boolean isEnabled= convertFromString(enabledString);
GeoServerUser u = createUserObject(username,password, isEnabled);
map.put(username, u);
}
ps.close();
rs.close();
ps = getDMLStatement("userprops.all",con);
rs = ps.executeQuery();
while (rs.next()) {
String useName = rs.getString(1);
String propName = rs.getString(2);
Object propValue = rs.getString(3);
GeoServerUser u = map.get(useName);
if (u!=null) {
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
SortedSet<GeoServerUser> users = new TreeSet<GeoServerUser>();
users.addAll(map.values());
return Collections.unmodifiableSortedSet(users);
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getUserGroups()
*/
public SortedSet<GeoServerUserGroup> getUserGroups() throws IOException{
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Collection<GeoServerUserGroup> tmp = new ArrayList<GeoServerUserGroup>();
try {
con = getConnection();
ps = getDMLStatement("groups.all",con);
rs = ps.executeQuery();
while (rs.next()) {
String groupname = rs.getString(1);
String enabledString = rs.getString(2);
boolean isEnabled= convertFromString(enabledString);
GeoServerUserGroup g = createGroupObject(groupname, isEnabled);
tmp.add(g);
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
SortedSet<GeoServerUserGroup> groups = new TreeSet<GeoServerUserGroup>();
groups.addAll(tmp);
return Collections.unmodifiableSortedSet(groups);
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#createUserObject(java.lang.String, java.lang.String, boolean)
*/
public GeoServerUser createUserObject(String username,String password, boolean isEnabled) throws IOException{
GeoServerUser user = new GeoServerUser(username);
user.setEnabled(isEnabled);
user.setPassword(password);
return user;
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#createGroupObject(java.lang.String, boolean)
*/
public GeoServerUserGroup createGroupObject(String groupname, boolean isEnabled) throws IOException{
GeoServerUserGroup group = new GeoServerUserGroup(groupname);
group.setEnabled(isEnabled);
return group;
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getGroupsForUser(org.geoserver.security.impl.GeoServerUser)
*/
public SortedSet<GeoServerUserGroup> getGroupsForUser (GeoServerUser user) throws IOException{
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Collection<GeoServerUserGroup> tmp = new ArrayList<GeoServerUserGroup>();
try {
con = getConnection();
ps = getDMLStatement("groupmembers.groupsForUser",con);
ps.setString(1, user.getUsername());
rs = ps.executeQuery();
while (rs.next()) {
String groupname = rs.getString(1);
String enabledString = rs.getString(2);
boolean isEnabled= convertFromString(enabledString);
GeoServerUserGroup g = createGroupObject(groupname, isEnabled);
tmp.add(g);
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
TreeSet<GeoServerUserGroup> groups = new TreeSet<GeoServerUserGroup>();
groups.addAll(tmp);
return Collections.unmodifiableSortedSet(groups);
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#getUsersForGroup(org.geoserver.security.impl.GeoServerUserGroup)
*/
public SortedSet<GeoServerUser> getUsersForGroup (GeoServerUserGroup group) throws IOException{
Connection con=null;
PreparedStatement ps = null,ps2 = null;
ResultSet rs = null,rs2=null;
Map<String,GeoServerUser> map = new HashMap<String,GeoServerUser>();
try {
con = getConnection();
ps = getDMLStatement("groupmembers.usersForGroup",con);
ps.setString(1, group.getGroupname());
rs = ps.executeQuery();
while (rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
String enabledString = rs.getString(3);
boolean isEnabled= convertFromString(enabledString);
GeoServerUser u = createUserObject(username,password, isEnabled);
map.put(username,u);
}
rs.close();
ps.close();
ps = getDMLStatement("userprops.userPropsForGroup",con);
ps.setString(1, group.getGroupname());
rs = ps.executeQuery();
while (rs.next()) {
String userName = rs.getString(1);
String propName = rs.getString(2);
Object propValue = rs.getObject(3);
GeoServerUser u = map.get(userName);
if (u!=null) {
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(null, ps2, rs2);
closeFinally(con, ps, rs);
}
TreeSet<GeoServerUser>users= new TreeSet<GeoServerUser>();
users.addAll(map.values());
return Collections.unmodifiableSortedSet(users);
}
public int getUserCount() throws IOException{
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
int count;
try {
con = getConnection();
ps = getDMLStatement("users.count",con);
rs = ps.executeQuery();
rs.next();
count=rs.getInt(1);
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return count;
}
public int getGroupCount() throws IOException{
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
int count;
try {
con = getConnection();
ps = getDMLStatement("groups.count",con);
rs = ps.executeQuery();
rs.next();
count=rs.getInt(1);
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return count;
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#load()
*/
public void load() throws IOException {
// do nothing
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#registerUserGroupChangedListener(org.geoserver.security.event.UserGroupChangedListener)
*/
public void registerUserGroupLoadedListener(UserGroupLoadedListener listener) {
listeners.add(listener);
}
/**
* @see org.geoserver.security.GeoServerUserGroupService#unregisterUserGroupChangedListener(org.geoserver.security.event.UserGroupChangedListener)
*/
public void unregisterUserGroupLoadedListener(UserGroupLoadedListener listener) {
listeners.remove(listener);
}
/**
* Fire {@link UserGroupLoadedEvent} for all listeners
*/
protected void fireUserGroupLoadedEvent() {
UserGroupLoadedEvent event = new UserGroupLoadedEvent(this);
for (UserGroupLoadedListener listener : listeners) {
listener.usersAndGroupsChanged(event);
}
}
/**
* The root configuration for the user group service.
*/
public Resource getConfigRoot() throws IOException {
return getSecurityManager().get("security/usergroup").get(getName());
}
/* (non-Javadoc)
* @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException,
DataAccessException {
GeoServerUser user=null;
try {
user = getUserByUsername(username);
if (user==null)
throw new UsernameNotFoundException(userNotFoundMessage(username));
RoleCalculator calculator = new RoleCalculator(this,
getSecurityManager().getActiveRoleService());
user.setAuthorities(calculator.calculateRoles(user));
} catch (IOException e) {
throw new UsernameNotFoundException(userNotFoundMessage(username),e);
}
return user;
}
protected String userNotFoundMessage(String username) {
return "User "+username +
" not found in usergroupservice: "+getName();
}
@Override
public SortedSet<GeoServerUser> getUsersHavingProperty(String propname) throws IOException {
if (StringUtils.hasLength(propname)==false)
return emptyUsers;
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Map<String,GeoServerUser> map = new HashMap<String,GeoServerUser>();
try {
con = getConnection();
ps = getDMLStatement("user.usersHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
while (rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
String enabledString = rs.getString(3);
boolean isEnabled= convertFromString(enabledString);
GeoServerUser u = createUserObject(username,password, isEnabled);
map.put(username, u);
}
ps.close();
rs.close();
ps = getDMLStatement("userprops.usersHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
while (rs.next()) {
String useName = rs.getString(1);
String propName = rs.getString(2);
Object propValue = rs.getString(3);
GeoServerUser u = map.get(useName);
if (u!=null) {
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
SortedSet<GeoServerUser> users = new TreeSet<GeoServerUser>();
users.addAll(map.values());
return Collections.unmodifiableSortedSet(users);
}
@Override
public int getUserCountHavingProperty(String propname) throws IOException {
if (StringUtils.hasLength(propname)==false)
return 0;
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
int count;
try {
con = getConnection();
ps = getDMLStatement("userprops.userCountHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
rs.next();
count=rs.getInt(1);
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return count;
}
@Override
public SortedSet<GeoServerUser> getUsersNotHavingProperty(String propname) throws IOException {
if (StringUtils.hasLength(propname)==false)
return emptyUsers;
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Map<String,GeoServerUser> map = new HashMap<String,GeoServerUser>();
try {
con = getConnection();
ps = getDMLStatement("user.usersNotHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
while (rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
String enabledString = rs.getString(3);
boolean isEnabled= convertFromString(enabledString);
GeoServerUser u = createUserObject(username,password, isEnabled);
map.put(username, u);
}
ps.close();
rs.close();
ps = getDMLStatement("userprops.usersNotHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
while (rs.next()) {
String useName = rs.getString(1);
String propName = rs.getString(2);
Object propValue = rs.getString(3);
GeoServerUser u = map.get(useName);
if (u!=null) {
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
SortedSet<GeoServerUser> users = new TreeSet<GeoServerUser>();
users.addAll(map.values());
return Collections.unmodifiableSortedSet(users);
}
@Override
public int getUserCountNotHavingProperty(String propname) throws IOException {
if (StringUtils.hasLength(propname)==false)
return getUserCount();
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
int count;
try {
con = getConnection();
ps = getDMLStatement("userprops.userCountNotHavingProperty",con);
ps.setString(1, propname);
rs = ps.executeQuery();
rs.next();
count=rs.getInt(1);
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return count;
}
@Override
public SortedSet<GeoServerUser> getUsersHavingPropertyValue(String propname, String propvalue)
throws IOException {
if (StringUtils.hasLength(propname)==false)
return emptyUsers;
if (StringUtils.hasLength(propvalue)==false)
return emptyUsers;
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
Map<String,GeoServerUser> map = new HashMap<String,GeoServerUser>();
try {
con = getConnection();
ps = getDMLStatement("user.usersHavingPropertyValue",con);
ps.setString(1,propname);
ps.setString(2,propvalue);
rs = ps.executeQuery();
while (rs.next()) {
String username = rs.getString(1);
String password = rs.getString(2);
String enabledString = rs.getString(3);
boolean isEnabled= convertFromString(enabledString);
GeoServerUser u = createUserObject(username,password, isEnabled);
map.put(username, u);
}
ps.close();
rs.close();
ps = getDMLStatement("userprops.usersHavingPropertyValue",con);
ps.setString(1, propname);
ps.setString(2,propvalue);
rs = ps.executeQuery();
while (rs.next()) {
String useName = rs.getString(1);
String propName = rs.getString(2);
Object propValue = rs.getString(3);
GeoServerUser u = map.get(useName);
if (u!=null) {
u.getProperties().put(propName, propValue==null ? "" : propValue);
}
}
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
SortedSet<GeoServerUser> users = new TreeSet<GeoServerUser>();
users.addAll(map.values());
return Collections.unmodifiableSortedSet(users);
}
@Override
public int getUserCountHavingPropertyValue(String propname, String propvalue)
throws IOException {
if (StringUtils.hasLength(propname)==false)
return 0;
if (StringUtils.hasLength(propvalue)==false)
return 0;
Connection con=null;
PreparedStatement ps = null;
ResultSet rs = null;
int count;
try {
con = getConnection();
ps = getDMLStatement("userprops.userCountHavingPropertyValue",con);
ps.setString(1, propname);
ps.setString(2, propvalue);
rs = ps.executeQuery();
rs.next();
count=rs.getInt(1);
} catch (SQLException ex) {
throw new IOException(ex);
} finally {
closeFinally(con, ps, rs);
}
return count;
}
}