diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java index 5f47a95997..1fefd4240b 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImpl.java @@ -38,6 +38,7 @@ import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfigur import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil; +import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; import org.jetbrains.annotations.NotNull; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -144,7 +145,7 @@ public class TokenConfigurationImpl extends ConfigurationBase implements TokenCo @NotNull @Override public List getValidators(@NotNull String workspaceName, @NotNull Set principals, @NotNull MoveTracker moveTracker) { - ValidatorProvider vp = new TokenValidatorProvider(getSecurityProvider().getParameters(UserConfiguration.NAME), getTreeProvider()); + ValidatorProvider vp = new TokenValidatorProvider(getSecurityProvider().getParameters(UserConfiguration.NAME), getTreeProvider(), principals); return ImmutableList.of(vp); } @@ -162,6 +163,12 @@ public class TokenConfigurationImpl extends ConfigurationBase implements TokenCo return new TokenProviderImpl(root, getParameters(), uc, newCredentialsSupport()); } + @NotNull + @Override + public List getProtectedItemImporters() { + return ImmutableList.of(new TokenImporter()); + } + @NotNull private CredentialsSupport newCredentialsSupport() { int size = credentialsSupport.size(); diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporter.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporter.java new file mode 100644 index 0000000000..184aa3cf39 --- /dev/null +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporter.java @@ -0,0 +1,122 @@ +/* + * 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.oak.security.authentication.token; + +import org.apache.jackrabbit.oak.api.Root; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.namepath.NamePathMapper; +import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; +import org.apache.jackrabbit.oak.spi.security.SecurityProvider; +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConstants; +import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal; +import org.apache.jackrabbit.oak.spi.xml.PropInfo; +import org.apache.jackrabbit.oak.spi.xml.ProtectedPropertyImporter; +import org.apache.jackrabbit.oak.spi.xml.ReferenceChangeTracker; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.PropertyDefinition; + +/** + * This ProtectedPropertyImporter is responsible for importing the rep:Token + * nodes. The rep:Token nodes are not protected, but all their properties are. + * + * It can work in two modes: if the provided Session is a system session, then + * the importer will rewrite all the token properties. Such a change is normally + * rejected by {@link TokenValidatorProvider}, but the validator accepts token + * modifications property made within system session. + * + * If it's not the system session, the token nodes are removed, as their required + * properties can't be set. + */ +public class TokenImporter implements ProtectedPropertyImporter, TokenConstants { + + private static final Logger log = LoggerFactory.getLogger(TokenImporter.class); + + private boolean isSystemSession; + + //----------------------------------------------< ProtectedItemImporter >--- + @Override + public boolean init(@NotNull Session session, @NotNull Root root, @NotNull NamePathMapper namePathMapper, boolean isWorkspaceImport, int uuidBehavior, @NotNull ReferenceChangeTracker referenceTracker, @NotNull SecurityProvider securityProvider) { + isSystemSession = root.getContentSession().getAuthInfo().getPrincipals().contains(SystemPrincipal.INSTANCE); + if (uuidBehavior == ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW) { + log.debug("ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW isn't for the token import"); + return false; + } + return true; + } + + @Override + public void processReferences() { + // nothing to + } + + //------------------------------------------< ProtectedPropertyImporter >--- + /** + * Only the system session is allowed to update the rep:Token properties. + * Otherwise the {@link org.apache.jackrabbit.oak.security.authentication.token.TokenValidatorProvider} + * will reject the change. + * + * If this importer works on top of system session, the properties will be + * set to parent. Otherwise method will do nothing and return false. + * + * @param parent The affected parent node. + * @param protectedPropInfo The {@code PropInfo} to be imported. + * @param def The property definition determined by the importer that + * calls this method. + * @return true if the parent is rep:Token and the importer was able to import + * the property; false otherwise + */ + @Override + public boolean handlePropInfo(@NotNull Tree parent, @NotNull PropInfo protectedPropInfo, @NotNull PropertyDefinition def) throws RepositoryException { + if (isTokenTree(parent)) { + if (isSystemSession) { + parent.setProperty(protectedPropInfo.asPropertyState(def)); + return true; + } else { + return false; + } + } else { + return false; + } + } + + /** + * If the importer doesn't work on system session, the whole rep:Token node + * should be removed from the imported content. + * + * @param protectedParent The protected parent tree. + */ + @Override + public void propertiesCompleted(@NotNull Tree protectedParent) throws IllegalStateException { + if (isTokenTree(protectedParent)) { + if (!isSystemSession) { + log.debug("Found {} node managed by system, => Removed from imported scope.", TOKEN_NT_NAME); + protectedParent.remove(); + } + } + + } + + private boolean isTokenTree(@NotNull Tree tree) { + return TOKEN_NT_NAME.equals(TreeUtil.getPrimaryTypeName(tree)); + } +} diff --git a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java index a6075094b5..040a42b830 100644 --- a/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java +++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorProvider.java @@ -30,6 +30,7 @@ import org.apache.jackrabbit.oak.spi.commit.ValidatorProvider; import org.apache.jackrabbit.oak.spi.commit.VisibleValidator; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConstants; +import org.apache.jackrabbit.oak.spi.security.principal.SystemPrincipal; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil; import org.apache.jackrabbit.oak.spi.state.NodeState; @@ -39,6 +40,9 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.security.Principal; +import java.util.Set; + import static com.google.common.base.Preconditions.checkState; class TokenValidatorProvider extends ValidatorProvider implements TokenConstants { @@ -49,8 +53,11 @@ class TokenValidatorProvider extends ValidatorProvider implements TokenConstants private final TreeProvider treeProvider; - TokenValidatorProvider(@NotNull ConfigurationParameters userConfig, @NotNull TreeProvider treeProvider) { + private final boolean isSystem; + + TokenValidatorProvider(@NotNull ConfigurationParameters userConfig, @NotNull TreeProvider treeProvider, @NotNull Set principals) { userRootPath = userConfig.getConfigValue(UserConstants.PARAM_USER_PATH, UserConstants.DEFAULT_USER_PATH); + isSystem = principals.contains(SystemPrincipal.INSTANCE); this.treeProvider = treeProvider; } @@ -156,7 +163,7 @@ class TokenValidatorProvider extends ValidatorProvider implements TokenConstants //--------------------------------------------------------< private >--- private void verifyCommitInfo() throws CommitFailedException { - if (!CommitMarker.isValidCommitInfo(commitInfo)) { + if (!CommitMarker.isValidCommitInfo(commitInfo) && !isSystem) { throw constraintViolation(63, "Attempt to manually create or change a token node or it's parent."); } } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java index 5e09ad1770..265f42df50 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java @@ -17,6 +17,8 @@ package org.apache.jackrabbit.oak; import java.security.Principal; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.List; import java.util.Set; @@ -28,6 +30,7 @@ import javax.jcr.SimpleCredentials; import javax.jcr.ValueFactory; import javax.jcr.security.AccessControlManager; import javax.jcr.security.Privilege; +import javax.security.auth.Subject; import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginException; @@ -60,6 +63,7 @@ import org.apache.jackrabbit.oak.security.internal.SecurityProviderBuilder; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.SecurityProvider; import org.apache.jackrabbit.oak.spi.security.authentication.ConfigurationUtil; +import org.apache.jackrabbit.oak.spi.security.authentication.SystemSubject; import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration; import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration; import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeConfiguration; @@ -182,6 +186,11 @@ public abstract class AbstractSecurityTest { return repository.login(getAdminCredentials(), null); } + @NotNull + protected ContentSession createSystemSession() throws PrivilegedActionException { + return Subject.doAs(SystemSubject.INSTANCE, (PrivilegedExceptionAction) () -> contentRepository.login(null, null)); + } + protected NamePathMapper getNamePathMapper() { return namePathMapper; } diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplTest.java index 44d07b17c7..a97af7d8c2 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenConfigurationImplTest.java @@ -26,6 +26,7 @@ import org.apache.jackrabbit.oak.spi.security.authentication.credentials.Credent import org.apache.jackrabbit.oak.spi.security.authentication.credentials.SimpleCredentialsSupport; import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration; import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenProvider; +import org.apache.jackrabbit.oak.spi.xml.ProtectedItemImporter; import org.junit.Test; import javax.jcr.Credentials; @@ -162,4 +163,15 @@ public class TokenConfigurationImplTest extends AbstractSecurityTest { verify(cs, times(2)).getAttributes(creds); } + + @Test + public void testGetProtectedItemImporters() { + TokenConfiguration config = getConfig(TokenConfiguration.class); + List importers = config.getProtectedItemImporters(); + + assertFalse(importers.isEmpty()); + assertEquals(1, importers.size()); + assertTrue(importers.get(0) instanceof TokenImporter); + } + } \ No newline at end of file diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporterTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporterTest.java new file mode 100644 index 0000000000..18772e7e74 --- /dev/null +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenImporterTest.java @@ -0,0 +1,114 @@ +/* + * 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.oak.security.authentication.token; + +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.oak.AbstractSecurityTest; +import org.apache.jackrabbit.oak.api.ContentSession; +import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Tree; +import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.plugins.memory.PropertyStates; +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConstants; +import org.apache.jackrabbit.oak.spi.xml.PropInfo; +import org.apache.jackrabbit.oak.spi.xml.ReferenceChangeTracker; +import org.junit.Test; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Session; +import javax.jcr.nodetype.PropertyDefinition; +import java.security.PrivilegedActionException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TokenImporterTest extends AbstractSecurityTest { + + private TokenImporter importer; + + @Override + public void before() throws Exception { + super.before(); + importer = new TokenImporter(); + } + + private boolean init(boolean systemSession, int uuidBehaviour) throws PrivilegedActionException { + ContentSession session; + if (systemSession) { + session = createSystemSession(); + } else { + session = adminSession; + } + return importer.init(mock(Session.class), session.getLatestRoot(), getNamePathMapper(), false, uuidBehaviour, new ReferenceChangeTracker(), getSecurityProvider()); + } + + @Test + public void testInitUnsupportedUuidBehaviour() throws PrivilegedActionException { + assertFalse(init(true, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW)); + } + + @Test + public void testInitSupportedUuidBehaviour() throws PrivilegedActionException { + // the TokenImporter can be initialized multiple times + assertTrue(init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING)); + assertTrue(init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING)); + assertTrue(init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW)); + } + + @Test + public void testProcessReferencesIsNop() throws PrivilegedActionException { + // no initialization required + importer.processReferences(); + + init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); + importer.processReferences(); + } + + @Test + public void testHandlePropInfoForTokenWithSystemSession() throws Exception { + init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + Tree tree = mockTreeWithPrimaryType(TokenConstants.TOKEN_NT_NAME); + + assertTrue(importer.handlePropInfo(tree, mock(PropInfo.class), mock(PropertyDefinition.class))); + } + + @Test + public void testHandlePropInfoForNonTokenWithSystemSession() throws Exception { + init(true, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + Tree tree = mockTreeWithPrimaryType(JcrConstants.NT_UNSTRUCTURED); + + assertFalse(importer.handlePropInfo(tree, mock(PropInfo.class), mock(PropertyDefinition.class))); + } + + @Test + public void testHandlePropInfoNonSystemSession() throws Exception { + init(false, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + Tree tree = mockTreeWithPrimaryType(TokenConstants.TOKEN_NT_NAME); + + assertFalse(importer.handlePropInfo(tree, mock(PropInfo.class), mock(PropertyDefinition.class))); + } + + private static Tree mockTreeWithPrimaryType(String primaryType) { + Tree tree = mock(Tree.class); + PropertyState state = PropertyStates.createProperty(JcrConstants.JCR_PRIMARYTYPE, primaryType, Type.NAME); + when(tree.getProperty(JcrConstants.JCR_PRIMARYTYPE)).thenReturn(state); + return tree; + } + +} \ No newline at end of file diff --git a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java index c990cbde94..e621043bff 100644 --- a/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java +++ b/oak-core/src/test/java/org/apache/jackrabbit/oak/security/authentication/token/TokenValidatorTest.java @@ -20,6 +20,7 @@ import java.util.Calendar; import java.util.Date; import java.util.UUID; +import com.google.common.collect.ImmutableSet; import org.apache.jackrabbit.JcrConstants; import org.apache.jackrabbit.oak.api.CommitFailedException; import org.apache.jackrabbit.oak.api.Tree; @@ -466,7 +467,7 @@ public class TokenValidatorTest extends AbstractTokenTest { // create a validator that has 'tokensTree' as parentBefore and parentAfter NodeState ns = getTreeProvider().asNodeState(tokensTree); TreeProvider tp = when(mock(TreeProvider.class).createReadOnlyTree(ns)).thenReturn(tokensTree).getMock(); - TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, tp); + TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, tp, ImmutableSet.of()); Validator v = tvp.getRootValidator(ns, ns, new CommitInfo("sid", "uid", CommitMarker.asCommitAttributes())); assertNotNull(v); v.childNodeChanged(tokenTree.getName(), mock(NodeState.class), mock(NodeState.class)); @@ -527,7 +528,7 @@ public class TokenValidatorTest extends AbstractTokenTest { @NotNull private Validator createRootValidator(@NotNull Tree before, @NotNull Tree after) { - TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, getTreeProvider()); + TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, getTreeProvider(), ImmutableSet.of()); Validator v = tvp.getRootValidator(getTreeProvider().asNodeState(before), getTreeProvider().asNodeState(after), new CommitInfo("sid", "uid", CommitMarker.asCommitAttributes())); assertNotNull(v); return v; @@ -535,7 +536,7 @@ public class TokenValidatorTest extends AbstractTokenTest { @NotNull private Validator createValidator(@NotNull Tree before, @NotNull Tree after, @NotNull String path, boolean isAdd) throws CommitFailedException { - TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, getTreeProvider()); + TokenValidatorProvider tvp = new TokenValidatorProvider(ConfigurationParameters.EMPTY, getTreeProvider(), ImmutableSet.of()); NodeState b = getTreeProvider().asNodeState(before); NodeState a = getTreeProvider().asNodeState(after); Validator v = tvp.getRootValidator(b, a, new CommitInfo("sid", "uid", CommitMarker.asCommitAttributes())); diff --git a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authentication/token/UserWithTokenImportTest.java b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authentication/token/UserWithTokenImportTest.java new file mode 100644 index 0000000000..1f00cd2ded --- /dev/null +++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/authentication/token/UserWithTokenImportTest.java @@ -0,0 +1,112 @@ +/* + * 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.oak.jcr.security.authentication.token; + +import org.apache.jackrabbit.oak.fixture.NodeStoreFixture; +import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest; +import org.apache.jackrabbit.oak.spi.security.authentication.SystemSubject; +import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConstants; +import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.junit.Test; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Node; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.security.auth.Subject; +import java.io.InputStream; +import java.security.PrivilegedExceptionAction; +import java.util.Calendar; + +import static org.apache.jackrabbit.JcrConstants.JCR_UUID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test XML import of users with persisted tokens. + */ +public class UserWithTokenImportTest extends AbstractRepositoryTest implements TokenConstants { + + private static final String XML_USER_WITH_TOKENS = "TokenImportTest-userWithTokens.xml"; + + public UserWithTokenImportTest(NodeStoreFixture fixture) { + super(fixture); + } + + @Test + public void importUserWithTokens() throws Exception { + Session s = null; + try { + s = createSession(false); + Node parent = doImport(s, UserConstants.DEFAULT_USER_PATH, XML_USER_WITH_TOKENS); + Node user = parent.getNode("t"); + Node tokens = user.getNode(".tokens"); + assertTokensAreMissing(tokens, "88dccc3c-6698-464f-a095-db0a046aa37e"); + s.save(); + } finally { + if (s != null) { + s.logout(); + } + } + } + + @Test + public void importUserWithTokensAsSystem() throws Exception { + Session s = null; + try { + s = createSession(true); + Node parent = doImport(s, UserConstants.DEFAULT_USER_PATH, XML_USER_WITH_TOKENS); + Node user = parent.getNode("t"); + Node tokens = user.getNode(".tokens"); + assertTokenExists(tokens, "88dccc3c-6698-464f-a095-db0a046aa37e"); + s.save(); + } finally { + if (s != null) { + s.logout(); + } + } + } + + private void assertTokensAreMissing(Node tokens, String tokenUuid) throws RepositoryException { + assertFalse(tokens.hasNode(tokenUuid)); + } + + private void assertTokenExists(Node tokens, String tokenUuid) throws RepositoryException { + assertTrue(tokens.hasNode(tokenUuid)); + Node token = tokens.getNode(tokenUuid); + assertEquals(tokenUuid, token.getProperty(JCR_UUID).getString()); + assertEquals("Custom property ip is missing", "127.0.0.1", token.getProperty("ip").getString()); + assertTrue("Token expiry is not in the future", token.getProperty(TOKEN_ATTRIBUTE_EXPIRY).getDate().after(Calendar.getInstance())); + assertEquals("Invalid token key", "{SHA-256}eadf03e7e25fda1d-3aa620e6365002f595334046bc2baee85920321a4f9b4a6ff942dd137c0e3420", token.getProperty(TOKEN_ATTRIBUTE_KEY).getString()); + } + + private Session createSession(boolean isSystem) throws Exception { + if (isSystem) { + return Subject.doAs(SystemSubject.INSTANCE, (PrivilegedExceptionAction) () -> getRepository().login(null, null)); + } else { + return getAdminSession(); + } + } + + private Node doImport(Session importSession, String parentPath, String xml) throws Exception { + try (InputStream in = getClass().getResourceAsStream(xml)) { + importSession.importXML(parentPath, in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW); + return importSession.getNode(parentPath); + } + } +} diff --git a/oak-jcr/src/test/resources/org/apache/jackrabbit/oak/jcr/security/authentication/token/TokenImportTest-userWithTokens.xml b/oak-jcr/src/test/resources/org/apache/jackrabbit/oak/jcr/security/authentication/token/TokenImportTest-userWithTokens.xml new file mode 100644 index 0000000000..764e416196 --- /dev/null +++ b/oak-jcr/src/test/resources/org/apache/jackrabbit/oak/jcr/security/authentication/token/TokenImportTest-userWithTokens.xml @@ -0,0 +1,17 @@ + + + rep:User + e358efa4-89f5-3062-b10d-d7316b65649e + t + tPrinc + + rep:Unstructured + + rep:Token + 88dccc3c-6698-464f-a095-db0a046aa37e + 127.0.0.1 + 2100-01-01T00:00:00.656Z + {SHA-256}eadf03e7e25fda1d-3aa620e6365002f595334046bc2baee85920321a4f9b4a6ff942dd137c0e3420 + + + \ No newline at end of file