You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by an...@apache.org on 2012/04/13 10:42:36 UTC
svn commit: r1325655 - in
/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr:
./ security/principal/ security/user/ util/
Author: angela
Date: Fri Apr 13 08:42:36 2012
New Revision: 1325655
URL: http://svn.apache.org/viewvc?rev=1325655&view=rev
Log:
OAK-50 - Implement User Management (WIP)
- re-adding ValueConverter#toScalar(String, int)
- making NodeImpl#getEditor public
Added:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/PrincipalIteratorAdapter.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableNodeCreator.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ItemBasedPrincipalImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/PasswordUtility.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt
Modified:
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java?rev=1325655&r1=1325654&r2=1325655&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/NodeImpl.java Fri Apr 13 08:42:36 2012
@@ -582,7 +582,19 @@ public class NodeImpl extends ItemImpl i
public boolean isNodeType(String nodeTypeName) throws RepositoryException {
checkStatus();
- // TODO
+ if (getProperty(JcrConstants.JCR_PRIMARYTYPE).getString().equals(nodeTypeName)) {
+ return true;
+ }
+ if (hasProperty(JcrConstants.JCR_MIXINTYPES)) {
+ Value[] mixins = getProperty(JcrConstants.JCR_MIXINTYPES).getValues();
+ for (Value mixin : mixins) {
+ if (mixin.getString().equals(nodeTypeName)) {
+ return true;
+ }
+ }
+ }
+
+ // TODO evaluate effective node type
return false;
}
@@ -815,6 +827,17 @@ public class NodeImpl extends ItemImpl i
}
+ //--------------------------------------------------------------------------
+ /**
+ * Access to KernelNodeStateEditor to allow code in other packages to
+ * access item states.
+ *
+ * @return The node state editor.
+ */
+ public NodeStateEditor getEditor() {
+ return getNodeState().getEditor();
+ }
+
//------------------------------------------------------------< private >---
/**
* Shortcut to retrieve the version manager from the workspace associated
@@ -846,10 +869,6 @@ public class NodeImpl extends ItemImpl i
return nodeState = getItemStateProvider().getNodeState(nodeState.getPath());
}
- private NodeStateEditor getEditor() {
- return getNodeState().getEditor();
- }
-
private String path() {
return getNodeState().getPath();
}
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/PrincipalIteratorAdapter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/PrincipalIteratorAdapter.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/PrincipalIteratorAdapter.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/principal/PrincipalIteratorAdapter.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.principal;
+
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorAdapter;
+import org.apache.jackrabbit.commons.iterator.RangeIteratorDecorator;
+
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * PrincipalIteratorAdapter...
+ *
+ * TODO: move to jackrabbit-jcr-commons
+ */
+public class PrincipalIteratorAdapter extends RangeIteratorDecorator
+ implements PrincipalIterator {
+
+ /**
+ * Static instance of an empty {@link PrincipalIterator}.
+ */
+ public static final PrincipalIteratorAdapter EMPTY = new PrincipalIteratorAdapter(RangeIteratorAdapter.EMPTY);
+
+
+ /**
+ * Creates an adapter for the given {@link java.util.Iterator} of principals.
+ *
+ * @param iterator iterator of {@link java.security.Principal}s
+ */
+ public PrincipalIteratorAdapter(Iterator<? extends Principal> iterator) {
+ super(new RangeIteratorAdapter(iterator));
+ }
+
+ /**
+ * Creates an iterator for the given collection of <code>Principal</code>s.
+ *
+ * @param collection collection of {@link Principal} objects.
+ */
+ public PrincipalIteratorAdapter(Collection<? extends Principal> collection) {
+ super(new RangeIteratorAdapter(collection));
+ }
+
+ //----------------------------------------< AccessControlPolicyIterator >---
+ /**
+ * Returns the next policy.
+ *
+ * @return next policy.
+ * @throws java.util.NoSuchElementException if there is no next policy.
+ */
+ public Principal nextPrincipal() throws NoSuchElementException {
+ return (Principal) next();
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,315 @@
+/*
+ * 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.user;
+
+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.commons.JcrUtils;
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.apache.jackrabbit.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.PropertyDefinition;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * AuthorizableImpl...
+ */
+abstract class AuthorizableImpl implements Authorizable {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(AuthorizableImpl.class);
+
+ static final String NT_REP_AUTHORIZABLE = "rep:Authorizable";
+ static final String NT_REP_USER = "rep:User";
+ static final String NT_REP_GROUP = "rep:Group";
+ static final String REP_PRINCIPAL_NAME = "rep:principalName";
+ static final String REP_PASSWORD = "rep:password";
+ static final String REP_DISABLED = "rep:disabled";
+ static final String REP_MEMBERS = "rep:members";
+ static final String REP_IMPERSONATORS = "rep:impersonators";
+
+ private final NodeImpl node;
+ private final UserManagerImpl userManager;
+
+ private int hashCode;
+
+ AuthorizableImpl(NodeImpl node, UserManagerImpl userManager) {
+ this.node = node;
+ this.userManager = userManager;
+ }
+
+ //-------------------------------------------------------< Authorizable >---
+ @Override
+ public String getID() throws RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public Iterator<Group> declaredMemberOf() throws RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public Iterator<Group> memberOf() throws RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public void remove() throws RepositoryException {
+ // don't allow for removal of the administrator even if the executing
+ // session has all permissions.
+ if (!isGroup() && ((User) this).isAdmin()) {
+ throw new RepositoryException("The administrator cannot be removed.");
+ }
+ Session s = node.getSession();
+ userManager.onRemove(this);
+ node.remove();
+ }
+
+ @Override
+ public Iterator<String> getPropertyNames() throws RepositoryException {
+ return getPropertyNames(".");
+ }
+
+ @Override
+ public Iterator<String> getPropertyNames(String relPath) throws RepositoryException {
+ Node n = node.getNode(relPath);
+ if (Text.isDescendantOrEqual(node.getPath(), n.getPath())) {
+ List<String> l = new ArrayList<String>();
+ for (PropertyIterator it = n.getProperties(); it.hasNext();) {
+ Property prop = it.nextProperty();
+ if (isAuthorizableProperty(prop, false)) {
+ l.add(prop.getName());
+ }
+ }
+ return l.iterator();
+ } else {
+ throw new IllegalArgumentException("Relative path " + relPath + " refers to items outside of scope of authorizable " + getID());
+ }
+ }
+
+ @Override
+ public boolean hasProperty(String relPath) throws RepositoryException {
+ return node.hasProperty(relPath) && isAuthorizableProperty(node.getProperty(relPath), true);
+ }
+
+ @Override
+ public Value[] getProperty(String relPath) throws RepositoryException {
+ Value[] values = null;
+ if (node.hasProperty(relPath)) {
+ Property prop = node.getProperty(relPath);
+ if (isAuthorizableProperty(prop, true)) {
+ if (prop.isMultiple()) {
+ values = prop.getValues();
+ } else {
+ values = new Value[]{prop.getValue()};
+ }
+ }
+ }
+ return values;
+ }
+
+ @Override
+ public void setProperty(String relPath, Value value) throws RepositoryException {
+ String name = Text.getName(relPath);
+ String intermediate = (relPath.equals(name)) ? null : Text.getRelativeParent(relPath, 1);
+
+ Node n = getOrCreateTargetNode(intermediate);
+ // check if the property has already been created as multi valued
+ // property before -> in this case remove in order to avoid
+ // ValueFormatException.
+ if (n.hasProperty(name)) {
+ Property p = n.getProperty(name);
+ if (p.isMultiple()) {
+ p.remove();
+ }
+ }
+ n.setProperty(name, value);
+ }
+
+ @Override
+ public void setProperty(String relPath, Value[] values) throws RepositoryException {
+ String name = Text.getName(relPath);
+ String intermediate = (relPath.equals(name)) ? null : Text.getRelativeParent(relPath, 1);
+
+ Node n = getOrCreateTargetNode(intermediate);
+ // check if the property has already been created as single valued
+ // property before -> in this case remove in order to avoid
+ // ValueFormatException.
+ if (n.hasProperty(name)) {
+ Property p = n.getProperty(name);
+ if (!p.isMultiple()) {
+ p.remove();
+ }
+ }
+ n.setProperty(name, values);
+ }
+
+ @Override
+ public boolean removeProperty(String relPath) throws RepositoryException {
+ String name = Text.getName(relPath);
+
+ if (node.hasProperty(relPath)) {
+ Property p = node.getProperty(relPath);
+ if (isAuthorizableProperty(p, true)) {
+ p.remove();
+ return true;
+ }
+ }
+ // no such property or wasn't a property of this authorizable.
+ return false;
+ }
+
+ @Override
+ public String getPath() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return node.getPath();
+ }
+
+ //-------------------------------------------------------------< Object >---
+ @Override
+ public int hashCode() {
+ if (hashCode == 0) {
+ try {
+ StringBuilder sb = new StringBuilder();
+ sb.append(isGroup() ? "group:" : "user:");
+ sb.append(node.getSession().getWorkspace().getName());
+ sb.append(':');
+ sb.append(node.getIdentifier());
+ hashCode = sb.toString().hashCode();
+ } catch (RepositoryException e) {
+ }
+ }
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof AuthorizableImpl) {
+ AuthorizableImpl otherAuth = (AuthorizableImpl) obj;
+ try {
+ return isGroup() == otherAuth.isGroup() && node.isSame(otherAuth.node);
+ } catch (RepositoryException e) {
+ // should not occur -> return false in this case.
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ String typeStr = (isGroup()) ? "Group '" : "User '";
+ return new StringBuilder().append(typeStr).append(getID()).append('\'').toString();
+ } catch (RepositoryException e) {
+ return super.toString();
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ /**
+ * @return node The node associated with this authorizable instance.
+ */
+ NodeImpl getNode() {
+ return node;
+ }
+
+ UserManagerImpl getUserManager() {
+ return userManager;
+ }
+
+ String getPrincipalName() throws RepositoryException {
+ String principalName;
+ if (node.hasProperty(REP_PRINCIPAL_NAME)) {
+ principalName = node.getProperty(REP_PRINCIPAL_NAME).getString();
+ } else {
+ log.debug("Authorizable without principal name -> using ID as fallback.");
+ principalName = getID();
+ }
+ return principalName;
+ }
+
+ /**
+ * Returns true if the given property of the authorizable node is one of the
+ * non-protected properties defined by the rep:Authorizable node type or a
+ * some other descendant of the authorizable node.
+ *
+ * @param prop Property to be tested.
+ * @param verifyAncestor If true the property is tested to be a descendant
+ * of the node of this authorizable; otherwise it is expected that this
+ * test has been executed by the caller.
+ * @return {@code true} if the given property is defined
+ * by the rep:authorizable node type or one of it's sub-node types;
+ * {@code false} otherwise.
+ * @throws RepositoryException If the property definition cannot be retrieved.
+ */
+ private boolean isAuthorizableProperty(Property prop, boolean verifyAncestor) throws RepositoryException {
+ if (verifyAncestor && !Text.isDescendant(node.getPath(), prop.getPath())) {
+ log.debug("Attempt to access property outside of authorizable scope.");
+ return false;
+ }
+
+ PropertyDefinition def = prop.getDefinition();
+ if (def.isProtected()) {
+ return false;
+ } else if (node.isSame(prop.getParent())) {
+ NodeType declaringNt = prop.getDefinition().getDeclaringNodeType();
+ return declaringNt.isNodeType(NT_REP_AUTHORIZABLE);
+ } else {
+ // another non-protected property somewhere in the subtree of this
+ // authorizable node -> is a property that can be set using #setProperty.
+ return true;
+ }
+ }
+
+ /**
+ *
+ * @param relPath A relative path.
+ * @return The corresponding node.
+ * @throws RepositoryException If an error occurs.
+ */
+ private Node getOrCreateTargetNode(String relPath) throws RepositoryException {
+ Node n;
+ if (relPath != null) {
+ n = JcrUtils.getOrCreateByPath(node, relPath, false, null, null, false);
+ if (!Text.isDescendantOrEqual(node.getPath(), n.getPath())) {
+ node.refresh(false);
+ throw new RepositoryException("Relative path " + relPath + " outside of scope of " + this);
+ }
+ } else {
+ n = node;
+ }
+ return n;
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableNodeCreator.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableNodeCreator.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableNodeCreator.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/AuthorizableNodeCreator.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,47 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.apache.jackrabbit.oak.jcr.SessionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * AuthorizableNodeCreator...
+ */
+class AuthorizableNodeCreator {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(AuthorizableNodeCreator.class);
+
+ AuthorizableNodeCreator(SessionContext sessionContext) {
+ // TODO
+ }
+
+ NodeImpl createUserNode(String userID, String intermediatePath) {
+ // TODO
+ return null;
+ }
+
+ NodeImpl createGroupNode(String groupID, String intermediatePath) {
+ // TODO
+ return null;
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/GroupImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,125 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.RepositoryException;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * GroupImpl...
+ */
+public class GroupImpl extends AuthorizableImpl implements Group {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(GroupImpl.class);
+
+ public GroupImpl(NodeImpl node, UserManagerImpl userManager) {
+ super(node, userManager);
+ }
+
+ //-------------------------------------------------------< Authorizable >---
+ @Override
+ public boolean isGroup() {
+ return true;
+ }
+
+ @Override
+ public Principal getPrincipal() throws RepositoryException {
+ return new GroupPrincipal(getPrincipalName(), getNode().getPath());
+ }
+
+ //--------------------------------------------------------------< Group >---
+
+ @Override
+ public Iterator<Authorizable> getDeclaredMembers() throws RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public Iterator<Authorizable> getMembers() throws RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public boolean isDeclaredMember(Authorizable authorizable) throws RepositoryException {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public boolean isMember(Authorizable authorizable) throws RepositoryException {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public boolean addMember(Authorizable authorizable) throws RepositoryException {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public boolean removeMember(Authorizable authorizable) throws RepositoryException {
+ // TODO
+ return false;
+ }
+
+ //--------------------------------------------------------------------------
+ /**
+ *
+ */
+ private class GroupPrincipal extends ItemBasedPrincipalImpl implements java.security.acl.Group {
+
+ GroupPrincipal(String principalName, String nodePath) {
+ super(principalName, getNode());
+ }
+
+ @Override
+ public boolean addMember(Principal principal) {
+ return false;
+ }
+
+ @Override
+ public boolean removeMember(Principal principal) {
+ return false;
+ }
+
+ @Override
+ public boolean isMember(Principal principal) {
+ // TODO
+ return false;
+ }
+
+ @Override
+ public Enumeration<? extends Principal> members() {
+ // TODO
+ return null;
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ImpersonationImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,212 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.oak.jcr.security.principal.PrincipalIteratorAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.security.auth.Subject;
+import java.security.Principal;
+import java.security.acl.Group;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * ImpersonationImpl...
+ */
+public class ImpersonationImpl implements Impersonation {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(ImpersonationImpl.class);
+
+ private static final String P_IMPERSONATORS = "rep:impersonators";
+
+ private final UserImpl user;
+
+ ImpersonationImpl(UserImpl user) throws RepositoryException {
+ this.user = user;
+ }
+
+ //------------------------------------------------------< Impersonation >---
+ /**
+ * @see org.apache.jackrabbit.api.security.user.Impersonation#getImpersonators()
+ */
+ @Override
+ public PrincipalIterator getImpersonators() throws RepositoryException {
+ Set<String> impersonators = getImpersonatorNames();
+ if (impersonators.isEmpty()) {
+ return PrincipalIteratorAdapter.EMPTY;
+ } else {
+ final PrincipalManager pMgr = getPrincipalManager();
+ Set<Principal> s = new HashSet<Principal>();
+ for (final String pName : impersonators) {
+ Principal p = pMgr.getPrincipal(pName);
+ if (p == null) {
+ log.debug("Impersonator " + pName + " does not correspond to a known Principal.");
+ p = new Principal() {
+ @Override
+ public String getName() {
+ return pName;
+ }
+ };
+ }
+ s.add(p);
+
+ }
+ return new PrincipalIteratorAdapter(s);
+ }
+ }
+
+ /**
+ * @see org.apache.jackrabbit.api.security.user.Impersonation#grantImpersonation(Principal)
+ */
+ @Override
+ public synchronized boolean grantImpersonation(Principal principal) throws RepositoryException {
+ String principalName = principal.getName();
+ PrincipalManager pMgr = getPrincipalManager();
+ if (!pMgr.hasPrincipal(principalName)) {
+ log.debug("Cannot grant impersonation to an unknown principal.");
+ return false;
+ }
+
+ Principal p = pMgr.getPrincipal(principalName);
+ if (p instanceof Group) {
+ log.debug("Cannot grant impersonation to a principal that is a Group.");
+ return false;
+ }
+
+ // make sure user does not impersonate himself
+ if (user.getPrincipal().getName().equals(principalName)) {
+ log.warn("Cannot grant impersonation to oneself.");
+ return false;
+ }
+
+ // make sure the given principal doesn't refer to the admin user.
+ Authorizable a = user.getUserManager().getAuthorizable(p);
+ if (a != null && user.getUserManager().isAdminId(a.getID())) {
+ log.debug("Admin principal is already granted impersonation.");
+ return false;
+ }
+
+ boolean granted = false;
+ Set<String> impersonators = getImpersonatorNames();
+ if (impersonators.add(principalName)) {
+ updateImpersonatorNames(impersonators);
+ granted = true;
+ }
+ return granted;
+ }
+
+ /**
+ * @see Impersonation#revokeImpersonation(java.security.Principal)
+ */
+ @Override
+ public synchronized boolean revokeImpersonation(Principal principal) throws RepositoryException {
+ boolean revoked = false;
+ String pName = principal.getName();
+
+ Set<String> impersonators = getImpersonatorNames();
+ if (impersonators.remove(pName)) {
+ updateImpersonatorNames(impersonators);
+ revoked = true;
+ }
+ return revoked;
+ }
+
+ /**
+ * @see Impersonation#allows(javax.security.auth.Subject)
+ */
+ @Override
+ public boolean allows(Subject subject) throws RepositoryException {
+ if (subject == null) {
+ return false;
+ }
+
+ Set<String> principalNames = new HashSet<String>();
+ for (Principal p : subject.getPrincipals()) {
+ principalNames.add(p.getName());
+ }
+
+ boolean allows;
+ Set<String> impersonators = getImpersonatorNames();
+ allows = impersonators.removeAll(principalNames);
+
+ if (!allows) {
+ // check if subject belongs to administrator user
+ for (Principal p : subject.getPrincipals()) {
+ if (p instanceof Group) {
+ continue;
+ }
+ UserManagerImpl userManager = getUserManagerImpl();
+ Authorizable a = userManager.getAuthorizable(p);
+ if (a != null && userManager.isAdminId(a.getID())) {
+ allows = true;
+ break;
+ }
+ }
+ }
+ return allows;
+ }
+
+ //------------------------------------------------------------< private >---
+
+ private Set<String> getImpersonatorNames() throws RepositoryException {
+ Set<String> princNames = new HashSet<String>();
+ if (user.getNode().hasProperty(P_IMPERSONATORS)) {
+ Value[] vs = user.getNode().getProperty(P_IMPERSONATORS).getValues();
+ for (Value v : vs) {
+ princNames.add(v.getString());
+ }
+ }
+ return princNames;
+ }
+
+ private void updateImpersonatorNames(Set<String> principalNames) throws RepositoryException {
+ String[] pNames = principalNames.toArray(new String[principalNames.size()]);
+ if (pNames.length == 0) {
+ user.getUserManager().removeInternalProperty(user.getNode(), P_IMPERSONATORS);
+ } else {
+ user.getUserManager().setInternalProperty(user.getNode(), P_IMPERSONATORS, pNames, PropertyType.STRING);
+ }
+ }
+
+ private PrincipalManager getPrincipalManager() throws RepositoryException {
+ Session s = user.getNode().getSession();
+ if (s instanceof JackrabbitSession) {
+ return ((JackrabbitSession) s).getPrincipalManager();
+ } else {
+ throw new UnsupportedRepositoryOperationException("Principal management not supported.");
+ }
+ }
+
+ private UserManagerImpl getUserManagerImpl() {
+ return user.getUserManager();
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ItemBasedPrincipalImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ItemBasedPrincipalImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ItemBasedPrincipalImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/ItemBasedPrincipalImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,92 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.api.security.principal.JackrabbitPrincipal;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Item;
+import javax.jcr.RepositoryException;
+import java.security.Principal;
+
+/**
+ * ItemBasedPrincipalImpl...
+ */
+class ItemBasedPrincipalImpl implements ItemBasedPrincipal {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(ItemBasedPrincipalImpl.class);
+
+ private final String principalName;
+ private final Item item;
+
+ ItemBasedPrincipalImpl(String principalName, Item item) {
+ this.principalName = principalName;
+ this.item = item;
+ }
+
+ //-------------------------------------------------< ItemBasedPrincipal >---
+ @Override
+ public String getPath() throws RepositoryException {
+ return item.getPath();
+ }
+
+ //----------------------------------------------------------< Principal >---
+ @Override
+ public String getName() {
+ return principalName;
+ }
+
+ //-------------------------------------------------------------< Object >---
+ /**
+ * Two principals are equal, if their names are.
+ * @see Object#equals(Object)
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof JackrabbitPrincipal) {
+ return principalName.equals(((Principal) obj).getName());
+ }
+ return false;
+ }
+
+ /**
+ * @return the hash code of the principals name.
+ * @see Object#hashCode()
+ */
+ @Override
+ public int hashCode() {
+ return principalName.hashCode();
+ }
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getName()).append(':').append(principalName);
+ return sb.toString();
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/PasswordUtility.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/PasswordUtility.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/PasswordUtility.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/PasswordUtility.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,60 @@
+/*
+ * 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.user;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * PasswordUtility...
+ */
+public class PasswordUtility {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(PasswordUtility.class);
+
+ public static final String DEFAULT_ALGORITHM = "SHA-256";
+ public static final int DEFAULT_SALT_SIZE = 8;
+ public static final int DEFAULT_ITERATIONS = 10000;
+
+ /**
+ * Avoid instanciation
+ */
+ private PasswordUtility() {};
+
+ public static boolean isSame(String passwordHash, String toTest) {
+ // TODO
+ return false;
+ }
+
+ public static String buildPasswordHash(String password, String algorithm,
+ int defaultSaltSize, int iterations)
+ throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ // TODO
+ return null;
+ }
+
+ public static boolean isPlainTextPassword(String password) {
+ // TODO
+ return false;
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,127 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.api.security.user.Impersonation;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Credentials;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import java.security.Principal;
+
+/**
+ * UserImpl...
+ */
+public class UserImpl extends AuthorizableImpl implements User {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(UserImpl.class);
+
+ UserImpl(NodeImpl node, UserManagerImpl userManager) {
+ super(node, userManager);
+ }
+
+ //-------------------------------------------------------< Authorizable >---
+ @Override
+ public boolean isGroup() {
+ return false;
+ }
+
+ @Override
+ public Principal getPrincipal() throws RepositoryException {
+ String principalName = getPrincipalName();
+ return new ItemBasedPrincipalImpl(principalName, getNode());
+
+ }
+
+ //---------------------------------------------------------------< User >---
+ @Override
+ public boolean isAdmin() {
+ try {
+ return getUserManager().isAdminId(getID());
+ } catch (RepositoryException e) {
+ // should never get here
+ log.error("Internal error while retrieving UserID.", e);
+ return false;
+ }
+ }
+
+ @Override
+ public Credentials getCredentials() throws RepositoryException {
+ throw new UnsupportedOperationException("Not implemented.");
+ }
+
+ @Override
+ public Impersonation getImpersonation() throws RepositoryException {
+ return new ImpersonationImpl(this);
+ }
+
+ @Override
+ public void changePassword(String password) throws RepositoryException {
+ UserManagerImpl userManager = getUserManager();
+ userManager.onPasswordChange(this, password);
+ userManager.setPassword(getNode(), password, true);
+ }
+
+ @Override
+ public void changePassword(String password, String oldPassword) throws RepositoryException {
+ // make sure the old password matches.
+ String pwHash = null;
+ if (getNode().hasProperty(REP_PASSWORD)) {
+ pwHash = getNode().getProperty(REP_PASSWORD).getString();
+ }
+ if (!PasswordUtility.isSame(pwHash, oldPassword)) {
+ throw new RepositoryException("Failed to change password: Old password does not match.");
+ }
+ changePassword(password);
+ }
+
+ @Override
+ public void disable(String reason) throws RepositoryException {
+ if (isAdmin()) {
+ throw new RepositoryException("The administrator user cannot be disabled.");
+ }
+ if (reason == null) {
+ if (isDisabled()) {
+ // enable the user again.
+ getUserManager().removeInternalProperty(getNode(), REP_DISABLED);
+ } // else: nothing to do.
+ } else {
+ getUserManager().setInternalProperty(getNode(), REP_DISABLED, reason, PropertyType.STRING);
+ }
+ }
+
+ @Override
+ public boolean isDisabled() throws RepositoryException {
+ return getNode().hasProperty(REP_DISABLED);
+ }
+
+ @Override
+ public String getDisabledReason() throws RepositoryException {
+ if (isDisabled()) {
+ return getNode().getProperty(REP_DISABLED).getString();
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerConfig.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,117 @@
+/*
+ * 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.user;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+/**
+ * UserManagerConfig...
+ */
+public class UserManagerConfig {
+
+ private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class);
+
+ /**
+ * Configuration parameter to change the default algorithm used to generate
+ * password hashes. The default value is {@link PasswordUtility#DEFAULT_ALGORITHM}.
+ */
+ public static final String PARAM_PASSWORD_HASH_ALGORITHM = "passwordHashAlgorithm";
+
+ /**
+ * Configuration parameter to change the number of iterations used for
+ * password hash generation. The default value is {@link PasswordUtility#DEFAULT_ITERATIONS}.
+ */
+ public static final String PARAM_PASSWORD_HASH_ITERATIONS = "passwordHashIterations";
+
+ /**
+ * Configuration parameter to change the number of iterations used for
+ * password hash generation. The default value is {@link PasswordUtility#DEFAULT_ITERATIONS}.
+ */
+ public static final String PARAM_PASSWORD_SALT_SIZE = "passwordSaltSize";
+
+ // TODO: check if that can really be node, who would retrieve it and what kind of access rights needed to be enforced on it
+ private final Node configNode;
+ private final String adminId;
+ //private final AuthorizableAction[] actions;
+
+ UserManagerConfig(Node configNode, String adminId) {
+ this.configNode = configNode;
+ this.adminId = adminId;
+ // this.actions = (actions == null) ? new AuthorizableAction[0] : actions;
+ }
+
+ public <T> T getConfigValue(String key, T defaultValue) {
+ try {
+ if (configNode.hasProperty(key)) {
+ return convert(configNode.getProperty(key), defaultValue);
+ }
+ } catch (RepositoryException e) {
+ // unexpected error -> return default value
+ log.debug(e.getMessage());
+ }
+ return defaultValue;
+ }
+
+ public String getAdminId() {
+ return adminId;
+ }
+
+// public AuthorizableAction[] getAuthorizableActions() {
+// return actions;
+// }
+
+ //--------------------------------------------------------< private >---
+ private static <T> T convert(Property configProperty, T defaultValue) throws RepositoryException {
+ T value;
+ String str;
+ // TODO properly deal with multi-value properties and array-default-values.
+ if (configProperty.isMultiple()) {
+ Value[] vls = configProperty.getValues();
+ str = (vls.length == 0) ? "" : vls[0].getString();
+ } else {
+ str = configProperty.getString();
+ }
+ Class targetClass = (defaultValue == null) ? String.class : defaultValue.getClass();
+ try {
+ if (targetClass == String.class) {
+ value = (T) str;
+ } else if (targetClass == Integer.class) {
+ value = (T) Integer.valueOf(str);
+ } else if (targetClass == Long.class) {
+ value = (T) Long.valueOf(str);
+ } else if (targetClass == Double.class) {
+ value = (T) Double.valueOf(str);
+ } else if (targetClass == Boolean.class) {
+ value = (T) Boolean.valueOf(str);
+ } else {
+ // unsupported target type
+ log.warn("Unsupported target type {} for config entry {}", targetClass.getName(), configProperty.getName());
+ throw new IllegalArgumentException("Cannot convert config entry " + configProperty.getName() + " to " + targetClass.getName());
+ }
+ } catch (NumberFormatException e) {
+ log.warn("Invalid value of config entry {}; cannot be parsed into {}", configProperty.getName(), targetClass.getName());
+ value = defaultValue;
+ }
+ return value;
+ }
+}
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/UserManagerImpl.java Fri Apr 13 08:42:36 2012
@@ -0,0 +1,365 @@
+/*
+ * 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.user;
+
+import org.apache.jackrabbit.api.security.principal.ItemBasedPrincipal;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.Query;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+import org.apache.jackrabbit.oak.api.Scalar;
+import org.apache.jackrabbit.oak.jcr.NodeImpl;
+import org.apache.jackrabbit.oak.jcr.SessionContext;
+import org.apache.jackrabbit.oak.jcr.util.ValueConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * UserManagerImpl...
+ */
+public class UserManagerImpl implements UserManager {
+
+ /**
+ * logger instance
+ */
+ private static final Logger log = LoggerFactory.getLogger(UserManagerImpl.class);
+
+ private final SessionContext sessionContext;
+ private final UserManagerConfig config;
+ private final AuthorizableNodeCreator nodeCreator;
+
+ public UserManagerImpl(SessionContext sessionContext, UserManagerConfig config) {
+ this.sessionContext = sessionContext;
+ this.config = config;
+ nodeCreator = new AuthorizableNodeCreator(sessionContext);
+ }
+
+ //--------------------------------------------------------< UserManager >---
+ /**
+ * @see UserManager#getAuthorizable(String)
+ */
+ @Override
+ public Authorizable getAuthorizable(String id) throws RepositoryException {
+ Authorizable authorizable = null;
+ try {
+ NodeImpl node = (NodeImpl) sessionContext.getSession().getNodeByIdentifier(buildIdentifier(id));
+ authorizable = getAuthorizable(node);
+ } catch (ItemNotFoundException e) {
+ log.debug("No authorizable with ID " + id);
+ }
+ return authorizable;
+ }
+
+ /**
+ * @see UserManager#getAuthorizable(Principal)
+ */
+ @Override
+ public Authorizable getAuthorizable(Principal principal) throws RepositoryException {
+ Session session = sessionContext.getSession();
+ Authorizable authorizable = null;
+ if (principal instanceof ItemBasedPrincipal) {
+ String authPath = ((ItemBasedPrincipal) principal).getPath();
+ if (session.nodeExists(authPath)) {
+ NodeImpl n = (NodeImpl) session.getNode(authPath);
+ authorizable = getAuthorizable(n);
+ }
+ } else {
+ // another Principal implementation.
+ String name = principal.getName();
+ Authorizable a = getAuthorizable(name);
+ if (a != null && name.equals(a.getPrincipal().getName())) {
+ authorizable = a;
+ } else {
+ Iterator<Authorizable> result = findAuthorizables(AuthorizableImpl.REP_PRINCIPAL_NAME, name, SEARCH_TYPE_AUTHORIZABLE);
+ if (result.hasNext()) {
+ authorizable = result.next();
+ }
+ }
+ }
+ // build the corresponding authorizable object
+ return authorizable;
+ }
+
+ @Override
+ public Authorizable getAuthorizableByPath(String path) throws UnsupportedRepositoryOperationException, RepositoryException {
+ // TODO
+ return null;
+ }
+
+ @Override
+ public Iterator<Authorizable> findAuthorizables(String relPath, String value) throws RepositoryException {
+ // TODO : create and execute a query
+ return null;
+ }
+
+ @Override
+ public Iterator<Authorizable> findAuthorizables(String relPath, String value, int searchType) throws RepositoryException {
+ // TODO : create and execute a query
+ return null;
+ }
+
+ @Override
+ public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException {
+ // TODO : execute the specified query
+ return null;
+ }
+
+ @Override
+ public User createUser(final String userID, String password) throws AuthorizableExistsException, RepositoryException {
+ Principal principal = new Principal() {
+ @Override
+ public String getName() {
+ return userID;
+ }
+ };
+ return createUser(userID, password, principal, null);
+ }
+
+ @Override
+ public User createUser(String userID, String password, Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ checkValidID(userID);
+
+ NodeImpl userNode = (NodeImpl) nodeCreator.createUserNode(userID, intermediatePath);
+ setPrincipal(userNode, principal);
+ setPassword(userNode, password, true);
+
+ User user = new UserImpl(userNode, this);
+ onCreate(user, password);
+
+ log.debug("User created: " + userID + "; " + userNode.getPath());
+ return user;
+ }
+
+ @Override
+ public Group createGroup(final String groupID) throws AuthorizableExistsException, RepositoryException {
+ Principal principal = new Principal() {
+ @Override
+ public String getName() {
+ return groupID;
+ }
+ };
+ return createGroup(groupID, principal, null);
+ }
+
+ @Override
+ public Group createGroup(Principal principal) throws AuthorizableExistsException, RepositoryException {
+ return createGroup(principal, null);
+ }
+
+ @Override
+ public Group createGroup(Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ String groupID = buildGroupID(principal.getName());
+ return createGroup(groupID, principal, intermediatePath);
+ }
+
+ @Override
+ public Group createGroup(String groupID, Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ checkValidID(groupID);
+
+ NodeImpl groupNode = (NodeImpl) nodeCreator.createGroupNode(groupID, intermediatePath);
+ setPrincipal(groupNode, principal);
+
+ Group group = new GroupImpl(groupNode, this);
+ onCreate(group);
+
+ log.debug("Group created: " + groupID + "; " + groupNode.getPath());
+ return group;
+ }
+
+ /**
+ * Always returns {@code false}. Any modifications made to this user
+ * manager instance require a subsequent call to {@link javax.jcr.Session#save()}
+ * in order to have the changes persisted.
+ *
+ * @see org.apache.jackrabbit.api.security.user.UserManager#isAutoSave()
+ */
+ @Override
+ public boolean isAutoSave() {
+ return false;
+ }
+
+ /**
+ * Changing the auto-save behavior is not supported by this implementation
+ * and this method always throws {@code UnsupportedRepositoryOperationException}
+ *
+ * @see UserManager#autoSave(boolean)
+ */
+ @Override
+ public void autoSave(boolean enable) throws UnsupportedRepositoryOperationException, RepositoryException {
+ throw new UnsupportedRepositoryOperationException("Session#save() is always required.");
+ }
+
+
+ //--------------------------------------------------------------------------
+ /**
+ * Let the configured <code>AuthorizableAction</code>s perform additional
+ * tasks associated with the creation of the new user before the
+ * corresponding new node is persisted.
+ *
+ * @param user The new user.
+ * @param pw The password.
+ * @throws RepositoryException If an exception occurs.
+ */
+ void onCreate(User user, String pw) throws RepositoryException {
+ // TODO
+ }
+
+ /**
+ * Let the configured <code>AuthorizableAction</code>s perform additional
+ * tasks associated with the creation of the new group before the
+ * corresponding new node is persisted.
+ *
+ * @param group The new group.
+ * @throws RepositoryException If an exception occurs.
+ */
+ void onCreate(Group group) throws RepositoryException {
+ // TODO
+ }
+
+ /**
+ * Let the configured <code>AuthorizableAction</code>s perform any clean
+ * up tasks related to the authorizable removal (before the corresponding
+ * node gets removed).
+ *
+ * @param authorizable The authorizable to be removed.
+ * @throws RepositoryException If an exception occurs.
+ */
+ void onRemove(Authorizable authorizable) throws RepositoryException {
+ // TODO
+ }
+
+ /**
+ * Let the configured <code>AuthorizableAction</code>s perform additional
+ * tasks associated with password changing of a given user before the
+ * corresponding property is being changed.
+ *
+ * @param user The target user.
+ * @param password The new password.
+ * @throws RepositoryException If an exception occurs.
+ */
+ void onPasswordChange(User user, String password) throws RepositoryException {
+ // TODO
+ }
+
+ //--------------------------------------------------------------------------
+ /**
+ * @param userID A userID.
+ * @return true if the given userID belongs to the administrator user.
+ */
+ boolean isAdminId(String userID) {
+ return config.getAdminId().equals(userID);
+ }
+
+ /**
+ *
+ *
+ * @param userNode The node representing the user.
+ * @param password The plaintext password to set.
+ * @param forceHash If true the specified password will always be hashed.
+ * @throws javax.jcr.RepositoryException If an error occurs
+ */
+ void setPassword(NodeImpl userNode, String password, boolean forceHash) throws RepositoryException {
+ if (password != null) {
+ log.debug("Password is null.");
+ return;
+ }
+ String pwHash;
+ if (forceHash || PasswordUtility.isPlainTextPassword(password)) {
+ try {
+ String algorithm = config.getConfigValue(UserManagerConfig.PARAM_PASSWORD_HASH_ALGORITHM, PasswordUtility.DEFAULT_ALGORITHM);
+ int iterations = config.getConfigValue(UserManagerConfig.PARAM_PASSWORD_HASH_ITERATIONS, PasswordUtility.DEFAULT_ITERATIONS);
+ int saltSize = config.getConfigValue(UserManagerConfig.PARAM_PASSWORD_SALT_SIZE, PasswordUtility.DEFAULT_SALT_SIZE);
+ pwHash = PasswordUtility.buildPasswordHash(password, algorithm, saltSize, iterations);
+ } catch (NoSuchAlgorithmException e) {
+ throw new RepositoryException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new RepositoryException(e);
+ }
+ } else {
+ pwHash = password;
+ }
+ setInternalProperty(userNode, AuthorizableImpl.REP_PASSWORD, pwHash, PropertyType.STRING);
+
+ }
+
+ void setPrincipal(NodeImpl userNode, Principal principal) throws RepositoryException {
+ // TODO: validate the principal
+
+ if (!userNode.isNew() || userNode.hasProperty(AuthorizableImpl.REP_PRINCIPAL_NAME)) {
+ throw new RepositoryException("rep:principalName can only be set once on a new node.");
+ }
+ setInternalProperty(userNode, AuthorizableImpl.REP_PRINCIPAL_NAME, principal.getName(), PropertyType.STRING);
+ }
+
+ void setInternalProperty(NodeImpl userNode, String name, String value, int type) throws RepositoryException {
+ // TODO: check again if this really makes a transient modification with marking the property modified/new
+ Scalar scalar = ValueConverter.toScalar(value, type);
+ userNode.getEditor().setProperty(name, scalar);
+ }
+
+ void setInternalProperty(NodeImpl userNode, String name, String[] values, int type) throws RepositoryException {
+ // TODO: check again if this really makes a transient modification with marking the property modified/new
+ List<Scalar> scalarList = new ArrayList<Scalar>(values.length);
+ for (int i = 0; i < values.length; i++) {
+ scalarList.add(ValueConverter.toScalar(values[i], PropertyType.STRING));
+ }
+ userNode.getEditor().setProperty(name, scalarList);
+ }
+
+ void removeInternalProperty(NodeImpl userNode, String name) {
+ // TODO: check again if this really makes a transient modification with marking the property modified
+ userNode.getEditor().removeProperty(name);
+ }
+
+ private Authorizable getAuthorizable(NodeImpl node) throws RepositoryException {
+ if (node.isNodeType(AuthorizableImpl.NT_REP_USER)) {
+ return new UserImpl(node, this);
+ } else if (node.isNodeType(AuthorizableImpl.NT_REP_GROUP)) {
+ return new GroupImpl(node, this);
+ } else {
+ throw new RepositoryException("Unexpected node type " + node.getPrimaryNodeType().getName() + ". Expected rep:User or rep:Group.");
+ }
+ }
+
+ private String buildIdentifier(String authorizableID) {
+ // TODO
+ return null;
+ }
+
+ private String buildGroupID(String principalName) {
+ // TODO
+ return principalName;
+ }
+
+ private void checkValidID(String authorizableID) {
+ // TODO
+ }
+}
\ No newline at end of file
Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt?rev=1325655&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/security/user/diff.txt Fri Apr 13 08:42:36 2012
@@ -0,0 +1,40 @@
+differences regarding jackrabbit 2.x implementation
+================================================================================
+
+General:
+- user management always associated with editing Session
+- changes made to user-mgt API are always transient + require Session#save()
+- in case of failure Session#refresh is not called anymore
+
+Unsupported Operations:
+- UserManager#autoSave(boolean)
+- User#getCredentials()
+
+UserManager:
+- UserManager#isAutoSave() always returns false
+- password not mandatory upon creation (TODO: check again)
+
+Authorizable:
+- setProperty, removeProperty executes no extra shortcut check for protected properties
+- not relying on rep:principalName to exist -> using ID as fallback
+-
+
+User:
+
+Group:
+
+
+TODO:
+================================================================================
+
+- Namespace handling on session level
+
+- Path handling that rely on oak-core implementation
+
+- Configuration based on oak-core implementation
+
+
+
+
+
+
Modified: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java?rev=1325655&r1=1325654&r2=1325655&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java (original)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/util/ValueConverter.java Fri Apr 13 08:42:36 2012
@@ -38,6 +38,37 @@ import java.util.List;
public final class ValueConverter {
private ValueConverter() {}
+ public static Scalar toScalar(String value, int propertyType) throws RepositoryException {
+ switch (propertyType) {
+ case PropertyType.STRING: {
+ return ScalarImpl.stringScalar(value);
+ }
+ case PropertyType.DOUBLE: {
+ return ScalarImpl.doubleScalar(Double.parseDouble(value));
+ }
+ case PropertyType.LONG: {
+ return ScalarImpl.longScalar(Long.parseLong(value));
+ }
+ case PropertyType.BOOLEAN: {
+ return ScalarImpl.booleanScalar(Boolean.parseBoolean(value));
+ }
+ case PropertyType.BINARY: {
+ return ScalarImpl.binaryScalar(value);
+ }
+ case PropertyType.DECIMAL:
+ case PropertyType.DATE:
+ case PropertyType.NAME:
+ case PropertyType.PATH:
+ case PropertyType.REFERENCE:
+ case PropertyType.WEAKREFERENCE:
+ case PropertyType.URI:
+ default: {
+ // todo implement toScalar
+ throw new UnsupportedRepositoryOperationException("toScalar");
+ }
+ }
+ }
+
public static Scalar toScalar(Value value) throws RepositoryException {
switch (value.getType()) {
case PropertyType.STRING: {