| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. 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. |
| */ |
| package org.apache.sling.jcr.contentloader.internal; |
| |
| import java.io.InputStream; |
| import java.io.UnsupportedEncodingException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.Principal; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.Stack; |
| import java.util.StringTokenizer; |
| |
| import javax.jcr.Binary; |
| import javax.jcr.Item; |
| import javax.jcr.ItemNotFoundException; |
| import javax.jcr.Node; |
| import javax.jcr.PropertyType; |
| import javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| import javax.jcr.Value; |
| import javax.jcr.ValueFactory; |
| import javax.jcr.version.VersionManager; |
| |
| import org.apache.jackrabbit.api.security.principal.PrincipalManager; |
| import org.apache.jackrabbit.api.security.user.Authorizable; |
| import org.apache.jackrabbit.api.security.user.Group; |
| import org.apache.jackrabbit.api.security.user.User; |
| import org.apache.jackrabbit.api.security.user.UserManager; |
| import org.apache.jackrabbit.util.ISO8601; |
| import org.apache.sling.jcr.base.util.AccessControlUtil; |
| import org.apache.sling.jcr.contentloader.ContentCreator; |
| import org.apache.sling.jcr.contentloader.ContentImportListener; |
| import org.apache.sling.jcr.contentloader.ContentReader; |
| import org.apache.sling.jcr.contentloader.ImportOptions; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The <code>ContentLoader</code> creates the nodes and properties. |
| * |
| * @since 2.0.4 |
| */ |
| public class DefaultContentCreator implements ContentCreator { |
| |
| final Logger log = LoggerFactory.getLogger(DefaultContentCreator.class); |
| |
| private ImportOptions configuration; |
| |
| private final Stack<Node> parentNodeStack = new Stack<Node>(); |
| |
| /** |
| * The list of versionables. |
| */ |
| private final List<Node> versionables = new ArrayList<Node>(); |
| |
| /** |
| * Delayed references during content loading for the reference property. |
| */ |
| private final Map<String, List<String>> delayedReferences = new HashMap<String, List<String>>(); |
| |
| private final Map<String, String[]> delayedMultipleReferences = new HashMap<String, String[]>(); |
| |
| private String defaultName; |
| |
| private Node createdRootNode; |
| |
| private boolean isParentNodeImport; |
| |
| private boolean ignoreOverwriteFlag = false; |
| |
| // default content type for createFile() |
| private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream"; |
| |
| /** |
| * Helper class to get the mime type of a file. |
| */ |
| private final ContentHelper contentHelper; |
| |
| /** |
| * List of active content readers mapped by extension. |
| */ |
| private Map<String, ContentReader> contentReaders; |
| |
| /** |
| * Optional list of created nodes (for uninstall) |
| */ |
| private List<String> createdNodes; |
| |
| /** |
| * Optional listener to get notified about changes |
| */ |
| private ContentImportListener importListener; |
| |
| /** |
| * A one time use seed to randomize the user location. |
| */ |
| private static final long INSTANCE_SEED = System.currentTimeMillis(); |
| |
| /** |
| * The number of levels folder used to store a user, could be a configuration option. |
| */ |
| private static final int STORAGE_LEVELS = 3; |
| |
| /** |
| * Constructor. |
| * |
| * @param contentHelper Helper class to get the mime type of a file |
| */ |
| public DefaultContentCreator(ContentHelper contentHelper) { |
| this.contentHelper = contentHelper; |
| } |
| |
| /** |
| * Initialize this component. |
| * |
| * @param pathEntry The configuration for this import. |
| * @param defaultContentReaders List of all content readers. |
| * @param createdNodes Optional list to store new nodes (for uninstall) |
| */ |
| public void init(final ImportOptions pathEntry, final Map<String, ContentReader> defaultContentReaders, final List<String> createdNodes, final ContentImportListener importListener) { |
| this.configuration = pathEntry; |
| // create list of allowed content readers |
| this.contentReaders = new HashMap<String, ContentReader>(); |
| final Iterator<Map.Entry<String, ContentReader>> entryIter = defaultContentReaders.entrySet().iterator(); |
| while (entryIter.hasNext()) { |
| final Map.Entry<String, ContentReader> current = entryIter.next(); |
| if (!configuration.isIgnoredImportProvider(current.getKey())) { |
| contentReaders.put(current.getKey(), current.getValue()); |
| } |
| } |
| this.createdNodes = createdNodes; |
| this.importListener = importListener; |
| } |
| |
| /** |
| * If the defaultName is null, we are in PARENT_NODE import mode. |
| * |
| * @param parentNode |
| * @param defaultName |
| */ |
| public void prepareParsing(final Node parentNode, final String defaultName) { |
| this.parentNodeStack.clear(); |
| this.parentNodeStack.push(parentNode); |
| this.defaultName = defaultName; |
| isParentNodeImport = defaultName == null; |
| this.createdRootNode = null; |
| } |
| |
| /** |
| * Get the list of versionable nodes. |
| */ |
| public List<Node> getVersionables() { |
| return this.versionables; |
| } |
| |
| /** |
| * Clear the content loader. |
| */ |
| public void clear() { |
| this.versionables.clear(); |
| } |
| |
| /** |
| * Set the ignore overwrite flag. |
| * |
| * @param flag |
| */ |
| public void setIgnoreOverwriteFlag(boolean flag) { |
| this.ignoreOverwriteFlag = flag; |
| } |
| |
| /** |
| * Get the created root node. |
| */ |
| public Node getCreatedRootNode() { |
| return this.createdRootNode; |
| } |
| |
| /** |
| * Get all active content readers. |
| * |
| * @return A map of readers |
| */ |
| public Map<String, ContentReader> getContentReaders() { |
| return this.contentReaders; |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createNode(java.lang.String, java.lang.String, java.lang.String[]) |
| */ |
| public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException { |
| final Node parentNode = this.parentNodeStack.peek(); |
| boolean isParentImport = (name == null && isParentNodeImport); |
| if (name == null) { |
| if (this.parentNodeStack.size() > 1) { |
| throw new RepositoryException("Node needs to have a name."); |
| } |
| name = this.defaultName; |
| } |
| |
| // if we are in parent node import mode, we don't create the root top level node! |
| if (!isParentImport || this.parentNodeStack.size() > 1) { |
| // if node already exists but should be overwritten, delete it |
| if (!this.ignoreOverwriteFlag && this.configuration.isOverwrite() && parentNode.hasNode(name)) { |
| checkoutIfNecessary(parentNode); |
| parentNode.getNode(name).remove(); |
| } |
| |
| // ensure repository node |
| Node node; |
| if (parentNode.hasNode(name)) { |
| // use existing node |
| node = parentNode.getNode(name); |
| } else if (primaryNodeType == null) { |
| // no explicit node type, use repository default |
| checkoutIfNecessary(parentNode); |
| node = parentNode.addNode(name); |
| addNodeToCreatedList(node); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getPath()); |
| } |
| } else { |
| // explicit primary node type |
| checkoutIfNecessary(parentNode); |
| node = parentNode.addNode(name, primaryNodeType); |
| addNodeToCreatedList(node); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getPath()); |
| } |
| } |
| |
| // amend mixin node types |
| if (mixinNodeTypes != null) { |
| for (final String mixin : mixinNodeTypes) { |
| if (!node.isNodeType(mixin)) { |
| node.addMixin(mixin); |
| } |
| } |
| } |
| |
| // check if node is versionable |
| final boolean addToVersionables = this.configuration.isCheckin() && node.isNodeType("mix:versionable"); |
| if (addToVersionables) { |
| this.versionables.add(node); |
| } |
| |
| this.parentNodeStack.push(node); |
| if (this.createdRootNode == null) { |
| this.createdRootNode = node; |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String) |
| */ |
| public void createProperty(String name, int propertyType, String value) throws RepositoryException { |
| final Node node = this.parentNodeStack.peek(); |
| // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case |
| if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) { |
| return; |
| } |
| |
| if (propertyType == PropertyType.REFERENCE) { |
| // need to resolve the reference |
| String propPath = node.getPath() + "/" + name; |
| String uuid = getUUID(node.getSession(), propPath, getAbsPath(node, value)); |
| if (uuid != null) { |
| checkoutIfNecessary(node); |
| node.setProperty(name, uuid, propertyType); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| } |
| } else if ("jcr:isCheckedOut".equals(name)) { |
| // don't try to write the property but record its state |
| // for later checkin if set to false |
| final boolean checkedout = Boolean.valueOf(value); |
| if (!checkedout) { |
| if (!this.versionables.contains(node)) { |
| this.versionables.add(node); |
| } |
| } |
| } else if (propertyType == PropertyType.DATE) { |
| checkoutIfNecessary(node); |
| node.setProperty(name, ISO8601.parse(value)); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| } else { |
| checkoutIfNecessary(node); |
| if (propertyType == PropertyType.UNDEFINED) { |
| node.setProperty(name, value); |
| } else { |
| node.setProperty(name, value, propertyType); |
| } |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String[]) |
| */ |
| public void createProperty(String name, int propertyType, String[] values) throws RepositoryException { |
| final Node node = this.parentNodeStack.peek(); |
| // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case |
| if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) { |
| return; |
| } |
| if (propertyType == PropertyType.REFERENCE) { |
| String propPath = node.getPath() + "/" + name; |
| boolean hasAll = true; |
| String[] uuids = new String[values.length]; |
| String[] uuidOrPaths = new String[values.length]; |
| for (int i = 0; i < values.length; i++) { |
| uuids[i] = getUUID(node.getSession(), propPath, getAbsPath(node, values[i])); |
| uuidOrPaths[i] = uuids[i] != null ? uuids[i] : getAbsPath(node, values[i]); |
| if (uuids[i] == null) hasAll = false; |
| } |
| checkoutIfNecessary(node); |
| node.setProperty(name, uuids, propertyType); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| if (!hasAll) { |
| delayedMultipleReferences.put(propPath, uuidOrPaths); |
| } |
| } else if (propertyType == PropertyType.DATE) { |
| checkoutIfNecessary(node); |
| |
| // This modification is to remove the colon in the JSON Timezone |
| ValueFactory valueFactory = node.getSession().getValueFactory(); |
| Value[] jcrValues = new Value[values.length]; |
| |
| for (int i = 0; i < values.length; i++) { |
| jcrValues[i] = valueFactory.createValue(ISO8601.parse(values[i])); |
| } |
| |
| node.setProperty(name, jcrValues, propertyType); |
| |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| } else { |
| checkoutIfNecessary(node); |
| if (propertyType == PropertyType.UNDEFINED) { |
| node.setProperty(name, values); |
| } else { |
| node.setProperty(name, values, propertyType); |
| } |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getProperty(name).getPath()); |
| } |
| } |
| } |
| |
| protected Value createValue(final ValueFactory factory, Object value) throws RepositoryException { |
| if (value == null) { |
| return null; |
| } |
| if (value instanceof Long) { |
| return factory.createValue((Long) value); |
| } else if (value instanceof Date) { |
| final Calendar c = Calendar.getInstance(); |
| c.setTime((Date) value); |
| return factory.createValue(c); |
| } else if (value instanceof Calendar) { |
| return factory.createValue((Calendar) value); |
| } else if (value instanceof Double) { |
| return factory.createValue((Double) value); |
| } else if (value instanceof Boolean) { |
| return factory.createValue((Boolean) value); |
| } else if (value instanceof InputStream) { |
| Binary binary = factory.createBinary((InputStream)value); |
| return factory.createValue(binary); |
| } else { |
| return factory.createValue(value.toString()); |
| } |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object) |
| */ |
| public void createProperty(String name, Object value) throws RepositoryException { |
| createProperty(name, value, false); |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object[]) |
| */ |
| public void createProperty(String name, Object[] values) throws RepositoryException { |
| createProperty(name, values, false); |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#finishNode() |
| */ |
| public void finishNode() throws RepositoryException { |
| final Node node = this.parentNodeStack.pop(); |
| // resolve REFERENCE property values pointing to this node |
| resolveReferences(node); |
| } |
| |
| private void addNodeToCreatedList(Node node) throws RepositoryException { |
| if (this.createdNodes != null) { |
| this.createdNodes.add(node.getSession().getWorkspace().getName() + ":" + node.getPath()); |
| } |
| } |
| |
| private String getAbsPath(Node node, String path) throws RepositoryException { |
| if (path.startsWith("/")) { |
| return path; |
| } |
| |
| while (path.startsWith("../")) { |
| path = path.substring(3); |
| node = node.getParent(); |
| } |
| |
| while (path.startsWith("./")) { |
| path = path.substring(2); |
| } |
| |
| return node.getPath() + "/" + path; |
| } |
| |
| private String getUUID(Session session, String propPath, String referencePath) throws RepositoryException { |
| if (session.itemExists(referencePath)) { |
| Item item = session.getItem(referencePath); |
| if (item.isNode()) { |
| Node refNode = (Node) item; |
| if (refNode.isNodeType("mix:referenceable")) { |
| return refNode.getIdentifier(); |
| } |
| } |
| } else { |
| // not existing yet, keep for delayed setting |
| List<String> current = delayedReferences.get(referencePath); |
| if (current == null) { |
| current = new ArrayList<String>(); |
| delayedReferences.put(referencePath, current); |
| } |
| current.add(propPath); |
| } |
| |
| // no UUID found |
| return null; |
| } |
| |
| private void resolveReferences(Node node) throws RepositoryException { |
| List<String> props = delayedReferences.remove(node.getPath()); |
| if (props == null || props.size() == 0) { |
| return; |
| } |
| |
| // check whether we can set at all |
| if (!node.isNodeType("mix:referenceable")) { |
| return; |
| } |
| |
| Session session = node.getSession(); |
| String uuid = node.getIdentifier(); |
| |
| for (String property : props) { |
| String name = getName(property); |
| Node parentNode = getParentNode(session, property); |
| if (parentNode != null) { |
| checkoutIfNecessary(parentNode); |
| if (parentNode.hasProperty(name) && parentNode.getProperty(name).getDefinition().isMultiple()) { |
| boolean hasAll = true; |
| String[] uuidOrPaths = delayedMultipleReferences.get(property); |
| String[] uuids = new String[uuidOrPaths.length]; |
| for (int i = 0; i < uuidOrPaths.length; i++) { |
| // is the reference still a path |
| if (uuidOrPaths[i].startsWith("/")) { |
| if (uuidOrPaths[i].equals(node.getPath())) { |
| uuidOrPaths[i] = uuid; |
| uuids[i] = uuid; |
| } else { |
| uuids[i] = null; |
| hasAll = false; |
| } |
| } else { |
| uuids[i] = uuidOrPaths[i]; |
| } |
| } |
| parentNode.setProperty(name, uuids, PropertyType.REFERENCE); |
| if (this.importListener != null) { |
| this.importListener.onCreate(parentNode.getProperty(name).getPath()); |
| } |
| if (hasAll) { |
| delayedMultipleReferences.remove(property); |
| } |
| } else { |
| parentNode.setProperty(name, uuid, PropertyType.REFERENCE); |
| if (this.importListener != null) { |
| this.importListener.onCreate(parentNode.getProperty(name).getPath()); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Gets the name part of the <code>path</code>. The name is |
| * the part of the path after the last slash (or the complete path if no |
| * slash is contained). |
| * |
| * @param path The path from which to extract the name part. |
| * @return The name part. |
| */ |
| private String getName(String path) { |
| int lastSlash = path.lastIndexOf('/'); |
| String name = (lastSlash < 0) ? path : path.substring(lastSlash + 1); |
| return name; |
| } |
| |
| private Node getParentNode(Session session, String path) throws RepositoryException { |
| int lastSlash = path.lastIndexOf('/'); |
| |
| // not an absolute path, cannot find parent |
| if (lastSlash < 0) { |
| return null; |
| } |
| |
| // node below root |
| if (lastSlash == 0) { |
| return session.getRootNode(); |
| } |
| |
| // item in the hierarchy |
| path = path.substring(0, lastSlash); |
| if (!session.itemExists(path)) { |
| return null; |
| } |
| |
| Item item = session.getItem(path); |
| return (item.isNode()) ? (Node) item : null; |
| } |
| |
| private void createProperty(String name, Object value, boolean overwriteExisting) throws RepositoryException { |
| final Node node = this.parentNodeStack.peek(); |
| // check if the property already exists, don't overwrite it in this case |
| if (node.hasProperty(name) && !node.getProperty(name).isNew() && !overwriteExisting) { |
| return; |
| } |
| if (value == null) { |
| if (node.hasProperty(name)) { |
| checkoutIfNecessary(node); |
| String propPath = node.getProperty(name).getPath(); |
| node.getProperty(name).remove(); |
| if (this.importListener != null) { |
| this.importListener.onDelete(propPath); |
| } |
| } |
| } else { |
| checkoutIfNecessary(node); |
| final Value jcrValue = this.createValue(node.getSession().getValueFactory(), value); |
| node.setProperty(name, jcrValue); |
| if (this.importListener != null) { |
| this.importListener.onModify(node.getProperty(name).getPath()); |
| } |
| } |
| } |
| |
| private void createProperty(String name, Object[] values, boolean overwriteExisting) throws RepositoryException { |
| final Node node = this.parentNodeStack.peek(); |
| // check if the property already exists, don't overwrite it in this case |
| if (node.hasProperty(name) && !node.getProperty(name).isNew() && !overwriteExisting) { |
| return; |
| } |
| if (values == null || values.length == 0) { |
| if (node.hasProperty(name)) { |
| checkoutIfNecessary(node); |
| String propPath = node.getProperty(name).getPath(); |
| node.getProperty(name).remove(); |
| if (this.importListener != null) { |
| this.importListener.onDelete(propPath); |
| } |
| } |
| } else { |
| checkoutIfNecessary(node); |
| final Value[] jcrValues = new Value[values.length]; |
| for (int i = 0; i < values.length; i++) { |
| jcrValues[i] = this.createValue(node.getSession().getValueFactory(), values[i]); |
| } |
| node.setProperty(name, jcrValues); |
| if (this.importListener != null) { |
| this.importListener.onModify(node.getProperty(name).getPath()); |
| } |
| } |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createFileAndResourceNode(java.lang.String, java.io.InputStream, java.lang.String, long) |
| */ |
| public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException { |
| int lastSlash = name.lastIndexOf('/'); |
| name = (lastSlash < 0) ? name : name.substring(lastSlash + 1); |
| final Node parentNode = this.parentNodeStack.peek(); |
| |
| // if node already exists but should be overwritten, delete it |
| if (parentNode.hasNode(name)) { |
| this.parentNodeStack.push(parentNode.getNode(name)); |
| Node contentNode = parentNode.getNode(name).getNode("jcr:content"); |
| this.parentNodeStack.push(contentNode); |
| long nodeLastModified = 0L; |
| if (contentNode.hasProperty("jcr:lastModified")) { |
| nodeLastModified = contentNode.getProperty("jcr:lastModified").getDate().getTimeInMillis(); |
| } |
| if (!this.configuration.isOverwrite() && nodeLastModified >= lastModified) { |
| return; |
| } |
| log.info("Updating {} lastModified:{} New Content LastModified:{}", new Object[]{parentNode.getNode(name).getPath(), new Date(nodeLastModified), new Date(lastModified)}); |
| } else { |
| this.createNode(name, "nt:file", null); |
| this.createNode("jcr:content", "nt:resource", null); |
| } |
| |
| // ensure content type |
| if (mimeType == null) { |
| mimeType = contentHelper.getMimeType(name); |
| if (mimeType == null) { |
| log.info("createFile: Cannot find content type for {}, using {}", name, DEFAULT_CONTENT_TYPE); |
| mimeType = DEFAULT_CONTENT_TYPE; |
| } |
| } |
| |
| // ensure sensible last modification date |
| if (lastModified <= 0) { |
| lastModified = System.currentTimeMillis(); |
| } |
| this.createProperty("jcr:mimeType", mimeType, true); |
| this.createProperty("jcr:lastModified", lastModified, true); |
| this.createProperty("jcr:data", data, true); |
| } |
| |
| /** |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#switchCurrentNode(java.lang.String, java.lang.String) |
| */ |
| public boolean switchCurrentNode(String subPath, String newNodeType) throws RepositoryException { |
| if (subPath.startsWith("/")) { |
| subPath = subPath.substring(1); |
| } |
| final StringTokenizer st = new StringTokenizer(subPath, "/"); |
| Node node = this.parentNodeStack.peek(); |
| while (st.hasMoreTokens()) { |
| final String token = st.nextToken(); |
| if (!node.hasNode(token)) { |
| if (newNodeType == null) { |
| return false; |
| } |
| checkoutIfNecessary(node); |
| final Node n = node.addNode(token, newNodeType); |
| addNodeToCreatedList(n); |
| if (this.importListener != null) { |
| this.importListener.onCreate(node.getPath()); |
| } |
| } |
| node = node.getNode(token); |
| } |
| this.parentNodeStack.push(node); |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createGroup(java.lang.String, java.lang.String[], java.util.Map) |
| */ |
| public void createGroup(final String name, String[] members, Map<String, Object> extraProperties) throws RepositoryException { |
| |
| final Node parentNode = this.parentNodeStack.peek(); |
| Session session = parentNode.getSession(); |
| |
| UserManager userManager = AccessControlUtil.getUserManager(session); |
| Authorizable authorizable = userManager.getAuthorizable(name); |
| if (authorizable == null) { |
| //principal does not exist yet, so create it |
| Group group = userManager.createGroup(new Principal() { |
| public String getName() { |
| return name; |
| } |
| }, |
| hashPath(name) |
| ); |
| authorizable = group; |
| } else { |
| //principal already exists, check to make sure it is the expected type |
| if (!authorizable.isGroup()) { |
| throw new RepositoryException("A user already exists with the requested name: " + name); |
| } |
| //group already exists so just update it below |
| } |
| //update the group members |
| if (members != null) { |
| Group group = (Group) authorizable; |
| for (String member : members) { |
| Authorizable memberAuthorizable = userManager.getAuthorizable(member); |
| if (memberAuthorizable != null) { |
| group.addMember(memberAuthorizable); |
| } |
| } |
| } |
| if (extraProperties != null) { |
| ValueFactory valueFactory = session.getValueFactory(); |
| Set<Entry<String, Object>> entrySet = extraProperties.entrySet(); |
| for (Entry<String, Object> entry : entrySet) { |
| Value value = createValue(valueFactory, entry.getValue()); |
| authorizable.setProperty(entry.getKey(), value); |
| } |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createUser(java.lang.String, java.lang.String, java.util.Map) |
| */ |
| public void createUser(final String name, String password, Map<String, Object> extraProperties) throws RepositoryException { |
| final Node parentNode = this.parentNodeStack.peek(); |
| Session session = parentNode.getSession(); |
| |
| UserManager userManager = AccessControlUtil.getUserManager(session); |
| Authorizable authorizable = userManager.getAuthorizable(name); |
| if (authorizable == null) { |
| //principal does not exist yet, so create it |
| User user = userManager.createUser(name, |
| password, |
| new Principal() { |
| public String getName() { |
| return name; |
| } |
| }, |
| hashPath(name) |
| ); |
| authorizable = user; |
| } else { |
| //principal already exists, check to make sure it is the expected type |
| if (authorizable.isGroup()) { |
| throw new RepositoryException("A group already exists with the requested name: " + name); |
| } |
| //user already exists so just update it below |
| } |
| if (extraProperties != null) { |
| ValueFactory valueFactory = session.getValueFactory(); |
| Set<Entry<String, Object>> entrySet = extraProperties.entrySet(); |
| for (Entry<String, Object> entry : entrySet) { |
| Value value = createValue(valueFactory, entry.getValue()); |
| authorizable.setProperty(entry.getKey(), value); |
| } |
| } |
| } |
| |
| /** |
| * @param item |
| * @return a parent path fragment for the item. |
| */ |
| protected String hashPath(String item) throws RepositoryException { |
| try { |
| String hash = digest("sha1", (INSTANCE_SEED + item).getBytes("UTF-8")); |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < STORAGE_LEVELS; i++) { |
| sb.append(hash, i * 2, (i * 2) + 2).append("/"); |
| } |
| return sb.toString(); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RepositoryException("Unable to hash the path.", e); |
| } catch (UnsupportedEncodingException e) { |
| throw new RepositoryException("Unable to hash the path.", e); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String, java.lang.String, java.lang.String[], java.lang.String[]) |
| */ |
| public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String order) throws RepositoryException { |
| final Node parentNode = this.parentNodeStack.peek(); |
| Session session = parentNode.getSession(); |
| |
| PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session); |
| Principal principal = principalManager.getPrincipal(principalId); |
| if (principal == null) { |
| // SLING-7268 - as pointed out in OAK-5496, we cannot successfully use PrincipalManager#getPrincipal in oak |
| // without the session that created the principal getting saved first (and a subsequent index update). |
| // Workaround by trying the UserManager#getAuthorizable API to locate the principal. |
| UserManager userManager = AccessControlUtil.getUserManager(session); |
| final Authorizable authorizable = userManager.getAuthorizable(principalId); |
| if (authorizable != null) { |
| principal = authorizable.getPrincipal(); |
| } |
| } |
| |
| if (principal == null) { |
| throw new RepositoryException("No principal found for id: " + principalId); |
| } |
| String resourcePath = parentNode.getPath(); |
| |
| if ((grantedPrivilegeNames != null) || (deniedPrivilegeNames != null)) { |
| AccessControlUtil.replaceAccessControlEntry(session, resourcePath, principal, grantedPrivilegeNames, deniedPrivilegeNames, null, order); |
| } |
| } |
| |
| /** |
| * used for the md5 |
| */ |
| private static final char[] hexTable = "0123456789abcdef".toCharArray(); |
| |
| /** |
| * Digest the plain string using the given algorithm. |
| * |
| * @param algorithm The alogrithm for the digest. This algorithm must be |
| * supported by the MessageDigest class. |
| * @param data the data to digest with the given algorithm |
| * @return The digested plain text String represented as Hex digits. |
| * @throws java.security.NoSuchAlgorithmException if the desired algorithm is not supported by |
| * the MessageDigest class. |
| */ |
| public static String digest(String algorithm, byte[] data) throws NoSuchAlgorithmException { |
| MessageDigest md = MessageDigest.getInstance(algorithm); |
| byte[] digest = md.digest(data); |
| StringBuffer res = new StringBuffer(digest.length * 2); |
| for (int i = 0; i < digest.length; i++) { |
| byte b = digest[i]; |
| res.append(hexTable[(b >> 4) & 15]); |
| res.append(hexTable[b & 15]); |
| } |
| return res.toString(); |
| } |
| |
| /** |
| * Find an ancestor that is versionable |
| */ |
| protected Node findVersionableAncestor(Node node) throws RepositoryException { |
| if (node == null) { |
| return null; |
| } else if (isVersionable(node)) { |
| return node; |
| } else { |
| try { |
| node = node.getParent(); |
| return findVersionableAncestor(node); |
| } catch (ItemNotFoundException e) { |
| // top-level |
| return null; |
| } |
| } |
| } |
| |
| protected boolean isVersionable(Node node) throws RepositoryException { |
| return node.isNodeType("mix:versionable"); |
| } |
| |
| /** |
| * Checkout the node if needed |
| */ |
| protected void checkoutIfNecessary(Node node) throws RepositoryException { |
| if (this.configuration.isAutoCheckout()) { |
| Node versionableNode = findVersionableAncestor(node); |
| if (versionableNode != null) { |
| if (!versionableNode.isCheckedOut()) { |
| VersionManager versionManager = versionableNode.getSession().getWorkspace().getVersionManager(); |
| versionManager.checkout(versionableNode.getPath()); |
| |
| if (this.importListener != null) { |
| this.importListener.onCheckout(versionableNode.getPath()); |
| } |
| } |
| } |
| } |
| } |
| |
| } |