/* (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.impl; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.SortedSet; import java.util.logging.Logger; import org.geoserver.platform.resource.Resource; import org.geoserver.security.GeoServerRoleService; import org.geoserver.security.GeoServerRoleStore; import org.geoserver.security.GeoServerUserGroupService; import org.geoserver.security.GeoServerUserGroupStore; import org.geoserver.security.password.GeoServerMultiplexingPasswordEncoder; import org.geoserver.security.password.GeoServerPasswordEncoder; import org.geoserver.security.validation.PasswordPolicyException; /** * Class for common methods * * * @author christian * */ public class Util { static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.security"); /** * Convert from string to boolean, use defaultValue * in case of null or empty string * * @param booleanString * @param defaultValue * */ static public boolean convertToBoolean(String booleanString, boolean defaultValue) { if (booleanString == null || booleanString.trim().length()==0) return defaultValue; return Boolean.valueOf(booleanString.trim()); } /** * Deep copy of the whole User/Group database * * @param service * @param store * @throws IOException */ static public void copyFrom(GeoServerUserGroupService service, GeoServerUserGroupStore store) throws IOException,PasswordPolicyException { GeoServerPasswordEncoder encoder = store.getSecurityManager().loadPasswordEncoder(store.getPasswordEncoderName()); encoder.initializeFor(store); GeoServerMultiplexingPasswordEncoder mEncoder = new GeoServerMultiplexingPasswordEncoder(store.getSecurityManager(),service); store.clear(); Map<String,GeoServerUser> newUserDict = new HashMap<String,GeoServerUser>(); Map<String,GeoServerUserGroup> newGroupDict = new HashMap<String,GeoServerUserGroup>(); for (GeoServerUser user : service.getUsers()) { String rawPassword = null; String encPassword = null; try { rawPassword = mEncoder.decode(user.getPassword()); encPassword= encoder.encodePassword(rawPassword, null); } catch (UnsupportedOperationException ex) { LOGGER.warning("Cannot recode user: "+user.getUsername()+" password: "+user.getPassword()); encPassword=user.getPassword(); } GeoServerUser newUser = store.createUserObject(user.getUsername(),encPassword, user.isEnabled()); for (Object key: user.getProperties().keySet()) { newUser.getProperties().put(key, user.getProperties().get(key)); } store.addUser(newUser); newUserDict.put(newUser.getUsername(),newUser); } for (GeoServerUserGroup group : service.getUserGroups()) { GeoServerUserGroup newGroup = store.createGroupObject(group.getGroupname(),group.isEnabled()); store.addGroup(newGroup); newGroupDict.put(newGroup.getGroupname(),newGroup); } for (GeoServerUserGroup group : service.getUserGroups()) { GeoServerUserGroup newGroup = newGroupDict.get(group.getGroupname()); for (GeoServerUser member : service.getUsersForGroup(group)) { GeoServerUser newUser = newUserDict.get(member.getUsername()); store.associateUserToGroup(newUser, newGroup); } } } /** * Deep copy of the whole role database * * @param service * @param store * @throws IOException */ static public void copyFrom(GeoServerRoleService service, GeoServerRoleStore store) throws IOException { store.clear(); Map<String,GeoServerRole> newRoleDict = new HashMap<String,GeoServerRole>(); for (GeoServerRole role : service.getRoles()) { GeoServerRole newRole = store.createRoleObject(role.getAuthority()); for (Object key: role.getProperties().keySet()) { newRole.getProperties().put(key, role.getProperties().get(key)); } store.addRole(newRole); newRoleDict.put(newRole.getAuthority(),newRole); } for (GeoServerRole role : service.getRoles()) { GeoServerRole parentRole = service.getParentRole(role); GeoServerRole newRole = newRoleDict.get(role.getAuthority()); GeoServerRole newParentRole = parentRole == null ? null : newRoleDict.get(parentRole.getAuthority()); store.setParentRole(newRole, newParentRole); } for (GeoServerRole role : service.getRoles()) { GeoServerRole newRole = newRoleDict.get(role.getAuthority()); SortedSet<String> usernames = service.getUserNamesForRole(role); for (String username : usernames) { store.associateRoleToUser(newRole, username); } SortedSet<String> groupnames = service.getGroupNamesForRole(role); for (String groupname : groupnames) { store.associateRoleToGroup(newRole, groupname); } } } public static String convertPropsToString(Properties props, String heading) { StringBuffer buff = new StringBuffer(); if (heading !=null) { buff.append(heading).append("\n\n"); } for (Entry<Object,Object> entry : props.entrySet()) { buff.append(entry.getKey().toString()).append(": ") .append(entry.getValue().toString()).append("\n"); } return buff.toString(); } /** * Determines if the the input stream is xml * if it is, use create properties loaded from xml * format, otherwise create properties from default * format. * * @param in * * @throws IOException */ public static Properties loadUniversal(InputStream in) throws IOException { final String xmlDeclarationStart = "<?xml"; BufferedInputStream bin = new BufferedInputStream(in); bin.mark(4096); BufferedReader reader = new BufferedReader(new InputStreamReader(bin)); String line = reader.readLine(); boolean isXML = line.startsWith(xmlDeclarationStart); bin.reset(); Properties props = new Properties(); if (isXML) props.loadFromXML(bin); else props.load(bin); return props; } /** * Reads a property file. * <p> * This method delegates to {@link #loadUniversal(InputStream)}. * </p> */ public static Properties loadPropertyFile(File f) throws IOException { FileInputStream fin = new FileInputStream(f); try { return loadUniversal(fin); } finally { fin.close(); } } /** * Reads a property file resource. * <p> * This method delegates to {@link #loadUniversal(InputStream)}. * </p> */ public static Properties loadPropertyFile(Resource f) throws IOException { try (InputStream in = f.in()) { return loadUniversal(in); } } /** * Tries recoding the old passwords. * * If it is not possible to retrieve the raw password (digest encoding, * empty encoding), the old encoding is used. * * If it is possible to retrieve the raw password, the password is recoded * using the actual password encoder * * @param store * @throws IOException */ public static void recodePasswords( GeoServerUserGroupStore store) throws IOException{ GeoServerPasswordEncoder encoder = store.getSecurityManager().loadPasswordEncoder(store.getPasswordEncoderName()); encoder.initializeFor(store); GeoServerMultiplexingPasswordEncoder mEncoder = new GeoServerMultiplexingPasswordEncoder(store.getSecurityManager(),store); for (GeoServerUser user : store.getUsers()) { if (encoder.isResponsibleForEncoding(user.getPassword())) continue; // nothing to do try { String rawpass= mEncoder.decode(user.getPassword()); // to avoid password policy exceptions, recode explicitly String encPass=encoder.encodePassword(rawpass, null); user.setPassword(encPass); try { store.updateUser(user); } catch (PasswordPolicyException e) { store.load(); // rollback throw new RuntimeException("Never should reach this point",e); } } catch (UnsupportedOperationException ex) { LOGGER.warning("Cannot recode user: "+user.getUsername()+ " with password: "+user.getPassword()); } } store.store(); } }