blob: 52d408577deaaaaabd974a0208f8b95efc38ba34 [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.oak.security.user;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import javax.jcr.RepositoryException;
import javax.jcr.UnsupportedRepositoryOperationException;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.oak.AbstractSecurityTest;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.ContentSession;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl;
import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
import org.apache.jackrabbit.oak.util.NodeUtil;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* @since OAK 1.0
*/
public class UserManagerImplTest extends AbstractSecurityTest {
private UserManagerImpl userMgr;
private String testUserId = "testUser";
private Set<String> beforeAuthorizables = new HashSet<String>();
@Before
public void before() throws Exception {
super.before();
userMgr = new UserManagerImpl(root, getPartialValueFactory(), getSecurityProvider());
beforeAuthorizables.clear();
Iterator<Authorizable> iter = userMgr.findAuthorizables("jcr:primaryType", null, UserManager.SEARCH_TYPE_AUTHORIZABLE);
while (iter.hasNext()) {
beforeAuthorizables.add(iter.next().getID());
}
}
@After
public void after() throws Exception {
Iterator<Authorizable> iter = userMgr.findAuthorizables("jcr:primaryType", null, UserManager.SEARCH_TYPE_AUTHORIZABLE);
while (iter.hasNext()) {
Authorizable auth = iter.next();
if (!beforeAuthorizables.remove(auth.getID())) {
try {
auth.remove();
} catch (RepositoryException e) {
// ignore
}
}
}
super.after();
}
/**
* @see <a href="https://issues.apache.org/jira/browse/OAK-3191">OAK-3191</a>
*/
@Test
public void testGetAuthorizableByEmptyId() throws Exception {
assertNull(userMgr.getAuthorizable(""));
}
/**
* @see <a href="https://issues.apache.org/jira/browse/OAK-3191">OAK-3191</a>
*/
@Test
public void testGetTypedAuthorizableByEmptyId() throws Exception {
assertNull(userMgr.getAuthorizable("", User.class));
}
/**
* @see <a href="https://issues.apache.org/jira/browse/OAK-3191">OAK-3191</a>
*/
@Test
public void testGetAuthorizableByNullId() throws Exception {
assertNull(userMgr.getAuthorizable((String) null));
}
/**
* @see <a href="https://issues.apache.org/jira/browse/OAK-3191">OAK-3191</a>
*/
@Test
public void testGetTypedAuthorizableByNullId() throws Exception {
assertNull(userMgr.getAuthorizable(null, User.class));
}
/**
* @see <a href="https://issues.apache.org/jira/browse/OAK-3191">OAK-3191</a>
*/
@Test
public void testGetTypedAuthorizableByNullPrincipal() throws Exception {
assertNull(userMgr.getAuthorizable((Principal) null));
}
@Test
public void testSetPassword() throws Exception {
User user = userMgr.createUser(testUserId, "pw");
root.commit();
List<String> pwds = new ArrayList<String>();
pwds.add("pw");
pwds.add("");
pwds.add("{sha1}pw");
Tree userTree = root.getTree(user.getPath());
for (String pw : pwds) {
userMgr.setPassword(userTree, testUserId, pw, true);
String pwHash = userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING);
assertNotNull(pwHash);
assertTrue(PasswordUtil.isSame(pwHash, pw));
}
for (String pw : pwds) {
userMgr.setPassword(userTree, testUserId, pw, false);
String pwHash = userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING);
assertNotNull(pwHash);
if (!pw.startsWith("{")) {
assertTrue(PasswordUtil.isSame(pwHash, pw));
} else {
assertFalse(PasswordUtil.isSame(pwHash, pw));
assertEquals(pw, pwHash);
}
}
}
@Test
public void setPasswordNull() throws Exception {
User user = userMgr.createUser(testUserId, null);
root.commit();
Tree userTree = root.getTree(user.getPath());
try {
userMgr.setPassword(userTree, testUserId, null, true);
fail("setting null password should fail");
} catch (NullPointerException e) {
// expected
}
try {
userMgr.setPassword(userTree, testUserId, null, false);
fail("setting null password should fail");
} catch (NullPointerException e) {
// expected
}
}
@Test
public void testGetPasswordHash() throws Exception {
User user = userMgr.createUser(testUserId, null);
root.commit();
Tree userTree = root.getTree(user.getPath());
assertNull(userTree.getProperty(UserConstants.REP_PASSWORD));
}
@Test
public void testIsAutoSave() throws Exception {
assertFalse(userMgr.isAutoSave());
}
@Test
public void testAutoSave() throws Exception {
try {
userMgr.autoSave(true);
fail("should fail");
} catch (UnsupportedRepositoryOperationException e) {
// success
}
}
@Test
public void testEnforceAuthorizableFolderHierarchy() throws RepositoryException, CommitFailedException {
User user = userMgr.createUser(testUserId, null);
root.commit();
NodeUtil userNode = new NodeUtil(root.getTree(user.getPath()));
NodeUtil folder = userNode.addChild("folder", UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
String path = folder.getTree().getPath();
// authNode - authFolder -> create User
try {
Principal p = new PrincipalImpl("test2");
userMgr.createUser(p.getName(), p.getName(), p, path);
root.commit();
fail("Users may not be nested.");
} catch (CommitFailedException e) {
// success
} finally {
Authorizable a = userMgr.getAuthorizable("test2");
if (a != null) {
a.remove();
root.commit();
}
}
NodeUtil someContent = userNode.addChild("mystuff", JcrConstants.NT_UNSTRUCTURED);
path = someContent.getTree().getPath();
try {
// authNode - anyNode -> create User
try {
Principal p = new PrincipalImpl("test3");
userMgr.createUser(p.getName(), p.getName(), p, path);
root.commit();
fail("Users may not be nested.");
} catch (CommitFailedException e) {
// success
} finally {
Authorizable a = userMgr.getAuthorizable("test3");
if (a != null) {
a.remove();
root.commit();
}
}
// authNode - anyNode - authFolder -> create User
folder = someContent.addChild("folder", UserConstants.NT_REP_AUTHORIZABLE_FOLDER);
root.commit(); // this time save node structure
try {
Principal p = new PrincipalImpl("test4");
userMgr.createUser(p.getName(), p.getName(), p, folder.getTree().getPath());
root.commit();
fail("Users may not be nested.");
} catch (CommitFailedException e) {
// success
} finally {
root.refresh();
Authorizable a = userMgr.getAuthorizable("test4");
if (a != null) {
a.remove();
root.commit();
}
}
} finally {
root.refresh();
Tree t = root.getTree(path);
if (t.exists()) {
t.remove();
root.commit();
}
}
}
@Test
public void testFindWithNullValue() throws RepositoryException {
Iterator<Authorizable> result = userMgr.findAuthorizables(UserConstants.REP_PRINCIPAL_NAME, null);
assertTrue(result.hasNext());
}
@Test
public void testFindWithNullValue2() throws RepositoryException {
Iterator<Authorizable> result = userMgr.findAuthorizables("./" + UserConstants.REP_PRINCIPAL_NAME, null);
assertTrue(result.hasNext());
}
@Test
public void testConcurrentCreateUser() throws Exception {
final List<Exception> exceptions = new ArrayList<Exception>();
List<Thread> workers = new ArrayList<Thread>();
for (int i=0; i<10; i++) {
final String userId = "foo-user-" + i;
workers.add(new Thread(new Runnable() {
public void run() {
try {
ContentSession admin = login(getAdminCredentials());
Root root = admin.getLatestRoot();
UserManager userManager = new UserManagerImpl(root, getPartialValueFactory(), getSecurityProvider());
userManager.createUser(userId, "pass");
root.commit();
admin.close();
} catch (Exception e) {
exceptions.add(e);
}
}
}));
}
for (Thread t : workers) {
t.start();
}
for (Thread t : workers) {
t.join();
}
for (Exception e : exceptions) {
e.printStackTrace();
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
}
/**
* Test related to OAK-1922: Asserting that the default behavior is such that
* no rep:pwd node is created upon user-creation.
*
* @since Oak 1.1
*/
@Test
public void testNewUserHasNoPwdNode() throws Exception {
String newUserId = "newuser" + UUID.randomUUID();
User user = null;
try {
user = getUserManager(root).createUser(newUserId, newUserId);
root.commit();
Assert.assertFalse(root.getTree(user.getPath()).hasChild(UserConstants.REP_PWD));
Assert.assertFalse(user.hasProperty(UserConstants.REP_PWD + "/" + UserConstants.REP_PASSWORD_LAST_MODIFIED));
} finally {
if (user != null) {
user.remove();
root.commit();
}
}
}
}