/* (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.xml; import static org.geoserver.security.xml.XMLConstants.A_GROUP_ENABLED_UR; import static org.geoserver.security.xml.XMLConstants.A_GROUP_NAME_UR; import static org.geoserver.security.xml.XMLConstants.A_MEMBER_NAME_UR; import static org.geoserver.security.xml.XMLConstants.A_PROPERTY_NAME_UR; import static org.geoserver.security.xml.XMLConstants.A_USER_ENABLED_UR; import static org.geoserver.security.xml.XMLConstants.A_USER_NAME_UR; import static org.geoserver.security.xml.XMLConstants.A_USER_PASSWORD_UR; import static org.geoserver.security.xml.XMLConstants.A_VERSION_UR; import static org.geoserver.security.xml.XMLConstants.E_GROUPS_UR; import static org.geoserver.security.xml.XMLConstants.E_GROUP_UR; import static org.geoserver.security.xml.XMLConstants.E_MEMBER_UR; import static org.geoserver.security.xml.XMLConstants.E_PROPERTY_UR; import static org.geoserver.security.xml.XMLConstants.E_USERREGISTRY_UR; import static org.geoserver.security.xml.XMLConstants.E_USERS_UR; import static org.geoserver.security.xml.XMLConstants.E_USER_UR; import static org.geoserver.security.xml.XMLConstants.NS_UR; import static org.geoserver.security.xml.XMLConstants.VERSION_UR_1_0; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.SortedSet; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.IOUtils; import org.geoserver.platform.resource.Resource; import org.geoserver.security.GeoServerUserGroupService; import org.geoserver.security.file.LockFile; import org.geoserver.security.impl.AbstractUserGroupStore; import org.geoserver.security.impl.GeoServerUser; import org.geoserver.security.impl.GeoServerUserGroup; import org.geoserver.security.validation.PasswordPolicyException; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * @author christian * */ public class XMLUserGroupStore extends AbstractUserGroupStore { static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geoserver.security.xml"); protected Resource userResource; //TODO: use resource locking system protected LockFile lockFile = null; /** * Validate against schema on load/store, * default = true; */ private boolean validatingXMLSchema = true; public boolean isValidatingXMLSchema() { return validatingXMLSchema; } public void setValidatingXMLSchema(boolean validatingXMLSchema) { this.validatingXMLSchema = validatingXMLSchema; } /* (non-Javadoc) * @see org.geoserver.security.GeoserverUserGroupStore#initializeFromServer(org.geoserver.security.GeoserverUserGroupService) */ public void initializeFromService(GeoServerUserGroupService service) throws IOException { this.userResource=((XMLUserGroupService) service).userResource; this.validatingXMLSchema=((XMLUserGroupService) service).isValidatingXMLSchema(); super.initializeFromService(service); } /* (non-Javadoc) * @see org.geoserver.security.impl.AbstractUserGroupStore#serialize() */ @Override protected void serialize() throws IOException { DocumentBuilder builder=null; try { DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance(); builder = fac.newDocumentBuilder(); } catch (ParserConfigurationException e1) { throw new IOException(e1); } Document doc =builder.newDocument(); Element userreg = doc.createElement(E_USERREGISTRY_UR); doc.appendChild(userreg); userreg.setAttribute(javax.xml.XMLConstants.XMLNS_ATTRIBUTE, NS_UR); userreg.setAttribute( A_VERSION_UR, VERSION_UR_1_0); Element users = doc.createElement(E_USERS_UR); userreg.appendChild(users); for (GeoServerUser userObject : helper.userMap.values()) { Element user = doc.createElement(E_USER_UR); users.appendChild(user); user.setAttribute( A_USER_NAME_UR, userObject.getUsername()); if (userObject.getPassword() != null) { user.setAttribute( A_USER_PASSWORD_UR, userObject.getPassword()); } user.setAttribute( A_USER_ENABLED_UR, String.valueOf(userObject.isEnabled())); for (Object key: userObject.getProperties().keySet()) { Element property = doc.createElement(E_PROPERTY_UR); user.appendChild(property); property.setAttribute(A_PROPERTY_NAME_UR, key.toString()); property.setTextContent(userObject.getProperties().getProperty(key.toString())); } } Element groups = doc.createElement(E_GROUPS_UR); userreg.appendChild(groups); for (GeoServerUserGroup groupObject : helper.groupMap.values()) { Element group = doc.createElement(E_GROUP_UR); groups.appendChild(group); group.setAttribute( A_GROUP_NAME_UR, groupObject.getGroupname()); group.setAttribute( A_GROUP_ENABLED_UR, groupObject.isEnabled() ? "true" : "false"); SortedSet<GeoServerUser> userObjects = helper.group_userMap.get(groupObject); if (userObjects !=null) { for (GeoServerUser userObject : userObjects) { Element member = doc.createElement(E_MEMBER_UR); group.appendChild(member); member.setAttribute( A_MEMBER_NAME_UR, userObject.getUsername()); } } } // serialize the dom try { // TODO, wait for JAVA 6 // if (isValidatingXMLSchema()) { // XMLValidator.Singleton.validateUserGroupRegistry(doc); // } Transformer tx = TransformerFactory.newInstance().newTransformer(); tx.setOutputProperty(OutputKeys.METHOD, "XML"); tx.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); tx.setOutputProperty(OutputKeys.INDENT, "yes"); OutputStream out = new BufferedOutputStream((userResource.out())); try { tx.transform(new DOMSource(doc), new StreamResult(out)); out.flush(); } finally { IOUtils.closeQuietly(out); } /* standard java, but there is no possiTbility to set * the number of chars to indent, each line is starting at * column 0 Source source = new DOMSource(doc); Result result = new StreamResult( new OutputStreamWriter(new FileOutputStream(userFile),"UTF-8")); Transformer xformer = TransformerFactory.newInstance().newTransformer(); xformer.setOutputProperty(OutputKeys.INDENT, "yes"); xformer.transform(source, result); */ } catch (Exception e) { throw new IOException(e); } } /* (non-Javadoc) * @see org.geoserver.security.impl.AbstractUserGroupService#deserialize() */ @Override protected void deserialize() throws IOException { super.deserialize(); releaseLock(); } protected void ensureLock() throws IOException { if (lockFile != null) return; // we have one lockFile = new LockFile(userResource); try { lockFile.writeLock(); } catch (IOException ex) { // cannot obtain lock lockFile = null; // assert lockFile == null throw ex; // throw again } } protected void releaseLock() { if (lockFile == null) return; // we have none lockFile.writeUnLock(); lockFile = null; } @Override public String toString() { return getName(); } @Override public void store() throws IOException { ensureLock(); super.store(); releaseLock(); } @Override public void addUser(GeoServerUser user) throws IOException,PasswordPolicyException { ensureLock(); super.addUser(user); } @Override public void addGroup(GeoServerUserGroup group) throws IOException { ensureLock(); super.addGroup(group); } @Override public void updateUser(GeoServerUser user) throws IOException,PasswordPolicyException { ensureLock(); super.updateUser(user); } @Override public void updateGroup(GeoServerUserGroup group) throws IOException { ensureLock(); super.updateGroup(group); } @Override public boolean removeUser(GeoServerUser user) throws IOException { ensureLock(); return super.removeUser(user); } @Override public boolean removeGroup(GeoServerUserGroup group) throws IOException { ensureLock(); return super.removeGroup(group); } @Override public void associateUserToGroup(GeoServerUser user, GeoServerUserGroup group) throws IOException { ensureLock(); super.associateUserToGroup(user, group); } @Override public void disAssociateUserFromGroup(GeoServerUser user, GeoServerUserGroup group) throws IOException { ensureLock(); super.disAssociateUserFromGroup(user, group); } @Override public void clear() throws IOException { ensureLock(); super.clear(); } @Override public GeoServerUser createUserObject(String username,String password, boolean isEnabled) throws IOException{ XMLGeoserverUser user = new XMLGeoserverUser(username); user.setEnabled(isEnabled); user.setPassword(password); return user; } }