blob: 3ca7b36b6e1f11b430a576abf0f6144f4ea2f065 [file] [log] [blame]
/*
* 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.jackrabbit.vault.packaging.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.IOException;
import java.security.Principal;
import javax.jcr.AccessDeniedException;
import javax.jcr.InvalidItemStateException;
import javax.jcr.ItemExistsException;
import javax.jcr.Node;
import javax.jcr.ReferentialIntegrityException;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.lock.LockException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.nodetype.NodeType;
import javax.jcr.version.VersionException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
import org.apache.jackrabbit.vault.fs.io.Archive;
import org.apache.jackrabbit.vault.fs.io.ImportOptions;
import org.apache.jackrabbit.vault.fs.io.Importer;
import org.apache.jackrabbit.vault.fs.io.JcrArchive;
import org.apache.jackrabbit.vault.fs.io.ZipArchive;
import org.apache.jackrabbit.vault.packaging.PackageException;
import org.apache.jackrabbit.vault.util.JcrConstants;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
/**
* {@code ImportTests}...
*/
public class ImportIT extends IntegrationTestBase {
public static final String TEST_ROOT = "/testroot";
public static final String ARCHIVE_ROOT = "/archiveroot";
@Before
public void init() {
clean(TEST_ROOT);
clean(ARCHIVE_ROOT);
}
@Test
public void testImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/tmp.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists("/tmp/foo/bar/tobi");
}
}
@Test
public void testReimportLess() throws IOException, RepositoryException, ConfigurationException {
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
try (Archive archive = getFileArchive("/test-packages/tmp.zip")) {
archive.open(true);
importer.run(archive, rootNode);
assertNodeExists("/tmp/foo/bar/tobi");
}
try (Archive archive = getFileArchive("/test-packages/tmp_less.zip")) {
archive.open(true);
importer.run(archive, rootNode);
assertNodeMissing("/tmp/foo/bar/tobi");
}
}
@Test
public void testFilteredImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/filtered_package.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists("/tmp");
assertNodeExists("/tmp/foo");
assertNodeExists("/tmp/foo/bar");
assertNodeExists("/tmp/foo/bar/tobi");
assertNodeMissing("/tmp/foo/bar/tom");
}
}
@Test
public void testUnFilteredImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/unfiltered_package.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists("/tmp");
assertNodeExists("/tmp/foo");
assertNodeExists("/tmp/foo/bar");
assertNodeExists("/tmp/foo/bar/tobi");
assertNodeExists("/tmp/foo/bar/tom");
}
}
@Test
public void testRelativeImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/tmp.zip")) {
admin.getRootNode().addNode(TEST_ROOT.substring(1, TEST_ROOT.length()));
admin.save();
archive.open(true);
Node rootNode = admin.getNode(TEST_ROOT);
ImportOptions opts = getDefaultOptions();
// manually creating filterPaths with correct coverage
WorkspaceFilter filter = archive.getMetaInf().getFilter();
for (PathFilterSet pathFilterSet : filter.getFilterSets()) {
pathFilterSet.setRoot(TEST_ROOT + pathFilterSet.getRoot());
}
opts.setFilter(filter);
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists(TEST_ROOT + "/tmp/foo/bar/tobi");
}
}
/**
* Imports an empty package with a filter "/testnode" relative to "/testnode". Since this is a relative import,
* the "/testnode" would map to "/testnode/testnode". So the import should not remove "/testnode".
*/
@Test
public void testRelativeEmptyImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/empty_testnode.zip")) {
admin.getRootNode().addNode(TEST_ROOT.substring(1, TEST_ROOT.length()));
admin.save();
archive.open(true);
Node rootNode = admin.getNode(TEST_ROOT);
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists(TEST_ROOT);
}
}
/**
* Creates an jcr archive at /archiveroot mapped to /testroot and imports it.
*/
@Test
public void testJcrArchiveImport() throws IOException, RepositoryException, ConfigurationException {
// create Jcr Archive
Node archiveNode = admin.getRootNode().addNode(ARCHIVE_ROOT.substring(1, ARCHIVE_ROOT.length()));
admin.save();
createNodes(archiveNode, 2, 4);
admin.save();
assertNodeExists(ARCHIVE_ROOT + "/n3/n3/n3");
try (JcrArchive archive = new JcrArchive(archiveNode, TEST_ROOT)) {
Node testRoot = admin.getRootNode().addNode(TEST_ROOT.substring(1, TEST_ROOT.length()));
testRoot.addNode("dummy", "nt:folder");
admin.save();
archive.open(true);
Node rootNode = admin.getNode(TEST_ROOT);
ImportOptions opts = getDefaultOptions();
//opts.setListener(new DefaultProgressListener());
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
admin.save();
assertNodeExists(TEST_ROOT + "/n3/n3/n3");
assertNodeMissing(TEST_ROOT + "dummy");
}
}
@Test
public void testConcurrentModificationHandling() throws IOException, RepositoryException, PackageException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/tags.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
opts.setAutoSaveThreshold(7);
Importer importer = new Importer(opts);
importer.setDebugFailAfterSave(2);
importer.run(archive, rootNode);
admin.save();
// count nodes
assertNodeExists("/etc/tags");
Node tags = admin.getNode("/etc/tags");
int numNodes = countNodes(tags);
assertEquals("Number of tags installed", 487, numNodes);
}
}
@Test
public void testSNSImport() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/test_sns.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
importer.run(archive, rootNode);
assertNodeExists("/tmp/testroot");
assertNodeExists("/tmp/testroot/foo");
assertProperty("/tmp/testroot/foo/name", "foo1");
// only check for SNS nodes if SNS supported
if (admin.getRepository().getDescriptorValue(Repository.NODE_TYPE_MANAGEMENT_SAME_NAME_SIBLINGS_SUPPORTED).getBoolean()) {
assertNodeExists("/tmp/testroot/foo[2]");
assertNodeExists("/tmp/testroot/foo[3]");
assertProperty("/tmp/testroot/foo[2]/name", "foo2");
assertProperty("/tmp/testroot/foo[3]/name", "foo3");
} else {
// otherwise nodes must not exist
assertNodeMissing("/tmp/testroot/foo[2]");
assertNodeMissing("/tmp/testroot/foo[3]");
}
}
}
@Test
public void testSubArchiveExtract() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/tmp_with_thumbnail.zip")) {
archive.open(true);
Node rootNode = admin.getRootNode();
Node tmpNode = rootNode.addNode("tmp");
Node fileNode = tmpNode.addNode("package.zip", "nt:file");
Node contentNode = fileNode.addNode("jcr:content", "nt:resource");
contentNode.setProperty("jcr:data", "");
contentNode.setProperty("jcr:lastModified", 0);
contentNode.addMixin("vlt:Package");
Node defNode = contentNode.addNode("vlt:definition", "vlt:PackageDefinition");
ImportOptions opts = getDefaultOptions();
Archive subArchive = archive.getSubArchive("META-INF/vault/definition", true);
DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
filter.add(new PathFilterSet(defNode.getPath()));
Importer importer = new Importer(opts);
importer.getOptions().setAutoSaveThreshold(Integer.MAX_VALUE);
importer.getOptions().setFilter(filter);
importer.run(subArchive, defNode);
admin.save();
assertFalse("Importer must not have any errors", importer.hasErrors());
assertNodeExists("/tmp/package.zip/jcr:content/vlt:definition/thumbnail.png");
}
}
@Test
public void testImportWithoutRootAccess() throws IOException, RepositoryException, ConfigurationException {
Assume.assumeTrue(!isOak());
// Create test user
UserManager userManager = ((JackrabbitSession)admin).getUserManager();
String userId = "user1";
String userPwd = "pwd1";
User user1 = userManager.createUser(userId, userPwd);
Principal principal1 = user1.getPrincipal();
// Create /tmp folder
admin.getRootNode().addNode("tmp");
admin.save();
// Setup test user ACLs such that the
// root node is not accessible
AccessControlUtils.addAccessControlEntry(admin, null, principal1, new String[]{"jcr:namespaceManagement","jcr:nodeTypeDefinitionManagement"}, true);
AccessControlUtils.addAccessControlEntry(admin, "/", principal1, new String[]{"jcr:all"}, false);
AccessControlUtils.addAccessControlEntry(admin, "/tmp", principal1, new String[]{"jcr:all"}, true);
admin.save();
// Import with a session associated to the test user
Session session = repository.login(new SimpleCredentials(userId, userPwd.toCharArray()));
ZipArchive archive = new ZipArchive(getFile("/test-packages/tmp.zip"));
archive.open(true);
ImportOptions opts = getDefaultOptions();
opts.setStrict(false);
Importer importer = new Importer(opts);
importer.run(archive, session, "/");
session.logout();
assertNodeExists("/tmp/foo/bar/tobi");
}
@Test
public void testImportWithoutRootAndTmpAccess() throws IOException, RepositoryException, ConfigurationException {
Assume.assumeTrue(!isOak());
// Create test user
UserManager userManager = ((JackrabbitSession)admin).getUserManager();
String userId = "user1";
String userPwd = "pwd1";
User user1 = userManager.createUser(userId, userPwd);
Principal principal1 = user1.getPrincipal();
// Create /tmp folder
admin.getRootNode().addNode("tmp").addNode("foo");
admin.save();
// Setup test user ACLs such that the
// root node is not accessible
AccessControlUtils.addAccessControlEntry(admin, null, principal1, new String[]{"jcr:namespaceManagement","jcr:nodeTypeDefinitionManagement"}, true);
AccessControlUtils.addAccessControlEntry(admin, "/", principal1, new String[]{"jcr:all"}, false);
AccessControlUtils.addAccessControlEntry(admin, "/tmp/foo", principal1, new String[]{"jcr:all"}, true);
admin.save();
// Import with a session associated to the test user
Session session = repository.login(new SimpleCredentials(userId, userPwd.toCharArray()));
ZipArchive archive = new ZipArchive(getFile("/test-packages/tmp_foo.zip"));
archive.open(true);
ImportOptions opts = getDefaultOptions();
opts.setStrict(false);
Importer importer = new Importer(opts);
importer.run(archive, session, "/");
session.logout();
assertNodeExists("/tmp/foo/bar/tobi");
}
@Test
public void testImportProtectedProperties() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/protected_properties.zip")) {
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
archive.open(true);
importer.run(archive, rootNode);
}
admin.save();
assertProperty("/testroot/jcr:createdBy", "admin"); // must have a different value than in the .content.xml as it is protected and set automatically
assertPropertyMissing("/testroot/someProtectedBooleanProperty"); // is protected and skipped in the import
assertProperty("/testroot/someUnprotectedStringProperty", "foo"); // is not protected and must be there
assertProperty("/testroot/someUnprotectedStringMvProperty", new String[0]);
}
@Test
@SuppressWarnings("java:S5783")
public void testImportWithPropertyConstraintViolation() throws IOException, RepositoryException, ConfigurationException {
try (Archive archive = getFileArchive("/test-packages/property_constraint_violation.zip")) {
Node rootNode = admin.getRootNode();
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
archive.open(true);
// we don't care whether constraint is immediately enforced or only on save() as both is valid according to JCR spec
RepositoryException e = Assert.assertThrows(RepositoryException.class, () -> { importer.run(archive, rootNode); admin.save(); });
assertEquals(ConstraintViolationException.class, ExceptionUtils.getRootCause(e).getClass());
}
}
@Test
// JCRVLT-557
public void testKeepNodeTypeForFolderAggregate() throws IOException, RepositoryException, ConfigurationException {
// create nodes which are covered by a folder aggregate with type nt:unstructured
Node rootNode = admin.getRootNode();
Node testrootNode = rootNode.addNode("testroot", NodeType.NT_UNSTRUCTURED);
testrootNode.addNode("myfolder", NodeType.NT_UNSTRUCTURED);
admin.save();
// first try a regular installation (which should fail)
ImportOptions opts = getDefaultOptions();
Importer importer = new Importer(opts);
try (Archive archive = getFileArchive("/test-packages/test_nt_unstructured_below_folder_aggregate.zip")) {
archive.open(true);
importer.run(archive, rootNode);
// admin.save();
Assert.fail("Installing the package should fail as it tries to install an nt:unstructured node below an nt:folder node");
} catch (RepositoryException e) {
// expected
}
admin.refresh(false);
// restore type of /testroot/myfolder
testrootNode.getNode("myfolder").setPrimaryType(JcrConstants.NT_UNSTRUCTURED/*NodeType.NT_UNSTRUCTURED*/); // TODO: somehow expanded names do not work in Oak (see https://issues.apache.org/jira/browse/OAK-9616)
admin.save();
// don't overwrite node types for folder aggregates (i.e. keep nt:unstructured instead of converting to nt:folder)
opts.setOverwritePrimaryTypesOfFolders(false);
importer = new Importer(opts);
// now installation should succeed
try (Archive archive = getFileArchive("/test-packages/test_nt_unstructured_below_folder_aggregate.zip")) {
archive.open(true);
importer.run(archive, rootNode);
}
admin.save();
// Checking for node types
// Behavior in 3.4.0: myfolder's node type covered by package folder aggregate is not touched
assertNodeHasPrimaryType("/testroot/myfolder", JcrConstants.NT_UNSTRUCTURED);
assertNodeHasPrimaryType("/testroot/myfolder/mychild", JcrConstants.NT_UNSTRUCTURED);
}
@Test
public void testEnhancedFileAggregatePackageWithIntermediateSaves() throws IOException, ConfigurationException, AccessDeniedException, ItemExistsException, ReferentialIntegrityException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
ImportOptions opts = getDefaultOptions();
opts.setAutoSaveThreshold(1); // auto-save after each deserialized aggregator
Importer importer = new Importer(opts);
try (Archive archive = getFileArchive("/test-packages/enhanced_file_aggregate.zip")) {
archive.open(true);
importer.run(archive, admin.getRootNode());
admin.save();
}
assertPropertyExists("/testroot/tika/config.xml/jcr:content/jcr:data");
assertProperty("/testroot/tika/config.xml/jcr:content/jcr:mimeType", "text/xml");
}
}