Package org.apache.jackrabbit.core.security.user

Source Code of org.apache.jackrabbit.core.security.user.AuthorizableImpl$NodeBasedPrincipal

/*
* 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.core.security.user;

import org.apache.jackrabbit.api.security.principal.PrincipalIterator;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
import org.apache.jackrabbit.api.security.principal.NoSuchPrincipalException;
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.User;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.PropertyImpl;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
import org.apache.jackrabbit.core.security.principal.ItemBasedPrincipal;
import org.apache.jackrabbit.core.security.principal.PrincipalImpl;
import org.apache.jackrabbit.core.security.principal.PrincipalIteratorAdapter;
import org.apache.jackrabbit.spi.Name;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.ItemNotFoundException;
import javax.jcr.nodetype.ConstraintViolationException;
import javax.jcr.nodetype.PropertyDefinition;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
* AuthorizableImpl
*/
abstract class AuthorizableImpl implements Authorizable, UserConstants {

    static final Logger log = LoggerFactory.getLogger(AuthorizableImpl.class);

    final UserManagerImpl userManager;
    private final NodeImpl node;

    /**
     * @param node    the Authorizable is persisted to.
     * @param userManager UserManager that created this Authorizable.
     * @throws IllegalArgumentException if the given node isn't of node type
     * {@link #NT_REP_AUTHORIZABLE}.
     * @throws RepositoryException If an error occurs.
     */
    protected AuthorizableImpl(NodeImpl node, UserManagerImpl userManager)
            throws RepositoryException {
        if (!node.isNodeType(NT_REP_AUTHORIZABLE)) {
            throw new IllegalArgumentException("Node argument of NodeType " + NT_REP_AUTHORIZABLE + " required");
        }
        this.node = node;
        this.userManager = userManager;
    }

    //-------------------------------------------------------< Authorizable >---
    /**
     * @see Authorizable#getPrincipals()
     */
    public PrincipalIterator getPrincipals() throws RepositoryException {
        Collection coll = new ArrayList();
        // the first element is the main principal of this user.
        coll.add(getPrincipal());
        // in addition add all referees.
        PrincipalManager prMgr = getSession().getPrincipalManager();
        for (Iterator it = getRefereeValues().iterator(); it.hasNext();) {
            String refName = ((Value) it.next()).getString();
            Principal princ = null;
            if (prMgr.hasPrincipal(refName)) {
                try {
                    princ = prMgr.getPrincipal(refName);
                } catch (NoSuchPrincipalException e) {
                    // should not get here
                }
            }
            if (princ == null) {
                log.warn("Principal "+ refName +" unknown to PrincipalManager.");
                princ = new PrincipalImpl(refName);
            }
            coll.add(princ);
        }
        return new PrincipalIteratorAdapter(coll);
    }

    /**
     * @see Authorizable#addReferee(Principal)
     */
    public synchronized boolean addReferee(Principal principal) throws RepositoryException {
        String principalName = principal.getName();
        Value princValue = getSession().getValueFactory().createValue(principalName);

        List refereeValues = getRefereeValues();
        if (refereeValues.contains(princValue) || getPrincipal().getName().equals(principalName)) {
            return false;
        }
        if (userManager.hasAuthorizableOrReferee(principal)) {
            throw new AuthorizableExistsException("Another authorizable already represented by or refeering to " +  principalName);
        }
        refereeValues.add(princValue);

        userManager.setProtectedProperty(node, P_REFEREES, (Value[]) refereeValues.toArray(new Value[refereeValues.size()]));
        return true;
    }

    /**
     * @see Authorizable#removeReferee(Principal)
     */
    public synchronized boolean removeReferee(Principal principal) throws RepositoryException {
        Value princValue = getSession().getValueFactory().createValue(principal.getName());
        List existingValues = getRefereeValues();

        if (existingValues.remove(princValue))  {
            PropertyImpl prop = node.getProperty(P_REFEREES);
            if (existingValues.isEmpty()) {
                userManager.removeProtectedItem(prop, node);
            } else {
                userManager.setProtectedProperty(node, P_REFEREES, (Value[]) existingValues.toArray(new Value[existingValues.size()]));
            }
            return true;
        }

        // specified principal was not referee of this authorizable.
        return false;
    }

    /**
     * @see Authorizable#declaredMemberOf()
     */
    public Iterator declaredMemberOf() throws RepositoryException {
        List memberShip = new ArrayList();
        collectMembership(memberShip, false);
        return memberShip.iterator();
    }

    /**
     * @see Authorizable#memberOf()
     */
    public Iterator memberOf() throws RepositoryException {
        List memberShip = new ArrayList();
        collectMembership(memberShip, true);
        return memberShip.iterator();
    }

    /**
     * @see Authorizable#getPropertyNames()
     */
    public Iterator getPropertyNames() throws RepositoryException {
        List l = new ArrayList();
        for (PropertyIterator it = node.getProperties(); it.hasNext();) {
            Property prop = it.nextProperty();
            if (isAuthorizableProperty(prop)) {
                l.add(prop.getName());
            }
        }
        return l.iterator();
    }

    /**
     * @see #getProperty(String)
     */
    public boolean hasProperty(String name) throws RepositoryException {
        return node.hasProperty(name) && isAuthorizableProperty(node.getProperty(name));
    }

    /**
     * @see #hasProperty(String)
     * @see Authorizable#getProperty(String)
     */
    public Value[] getProperty(String name) throws RepositoryException {
        if (hasProperty(name)) {
            Property prop = node.getProperty(name);
            if (isAuthorizableProperty(prop)) {
                if (prop.getDefinition().isMultiple()) {
                    return prop.getValues();
                } else {
                    return new Value[] {prop.getValue()};
                }
            }
        }
        return null;
    }

    /**
     * Sets the Value for the given name. If a value existed, it is replaced,
     * if not it is created.
     *
     * @param name The property name.
     * @param value The property value.
     * @throws RepositoryException If the specified name defines a property
     * that needs to be modified by this user API or setting the corresponding
     * JCR property fails.
     * @see Authorizable#setProperty(String, Value)
     */
    public synchronized void setProperty(String name, Value value) throws RepositoryException {
        checkProtectedProperty(name);
        try {
            node.setProperty(name, value);
            node.save();
        } catch (RepositoryException e) {
            log.warn("Failed to set Property " + name + " for Authorizable " + getID());
            node.refresh(false);
            throw e;
        }
    }

    /**
     * Sets the Value[] for the given name. If a value existed, it is replaced,
     * if not it is created.
     *
     * @param name The property name.
     * @param values The property values.
     * @throws RepositoryException If the specified name defines a property
     * that needs to be modified by this user API or setting the corresponding
     * JCR property fails.
     * @see Authorizable#setProperty(String, Value[])
     */
    public synchronized void setProperty(String name, Value[] values) throws RepositoryException {
        checkProtectedProperty(name);
        try {
            node.setProperty(name, values);
            node.save();
        } catch (RepositoryException e) {
            log.warn("Failed to set Property " + name + " for Authorizable " + getID());
            node.refresh(false);
            throw e;
        }
    }
    /**
     * @see Authorizable#removeProperty(String)
     */
    public synchronized boolean removeProperty(String name) throws RepositoryException {
        checkProtectedProperty(name);
        try {
            if (node.hasProperty(name)) {
                // 'node' is protected -> use setValue instead of Property.remove()
                Property p = node.getProperty(name);
                if (p.getDefinition().isMultiple()) {
                    p.setValue((Value[]) null);
                } else {
                    p.setValue((Value) null);
                }
                node.save();
                return true;
            } else {
                return false;
            }
        } catch (RepositoryException e) {
            log.warn("Failed to remove Property " + name + " from Authorizable " + getID());
            node.refresh(false);
            throw e;
        }
    }

    /**
     * @see Authorizable#remove()
     */
    public synchronized 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.");
        }
        userManager.removeProtectedItem(node, node.getParent());
    }

    //--------------------------------------------------------------------------
    /**
     * @return node The underlying <code>Node</code> object.
     */
    NodeImpl getNode() {
        return node;
    }

    SessionImpl getSession() throws RepositoryException {
        return (SessionImpl) node.getSession();
    }

    String getPrincipalName() throws RepositoryException {
        // principal name is mandatory property -> no check required.
        return node.getProperty(P_PRINCIPAL_NAME).getString();
    }

    boolean addToGroup(GroupImpl group) throws RepositoryException {
        try {
            Value[] values;
            Value added = getSession().getValueFactory().createValue(group.getNode(), true);
            NodeImpl node = getNode();
            if (node.hasProperty(P_GROUPS)) {
                Value[] old = node.getProperty(P_GROUPS).getValues();
                values = new Value[old.length + 1];
                System.arraycopy(old, 0, values, 0, old.length);
            } else {
                values = new Value[1];
            }
            values[values.length - 1] = added;
            userManager.setProtectedProperty(node, P_GROUPS, values);
            return true;
        } catch (RepositoryException e) {
            // revert all pending changes and rethrow.
            log.warn("Error while editing group membership:", e.getMessage());
            getSession().refresh(false);
            throw e;
        }
    }

    boolean removeFromGroup(GroupImpl group) throws RepositoryException {
        NodeImpl node = getNode();
        String message = "Authorizable " + getID() + " is not member of " + group.getID();
        if (!node.hasProperty(P_GROUPS)) {
            log.debug(message);
            return false;
        }

        Value toRemove = getSession().getValueFactory().createValue(group.getNode(), true);
        PropertyImpl property = node.getProperty(P_GROUPS);
        List valList = new ArrayList(Arrays.asList(property.getValues()));
        if (valList.remove(toRemove)) {
            try {
                if (valList.isEmpty()) {
                    userManager.removeProtectedItem(property, node);
                } else {
                    Value[] values = (Value[]) valList.toArray(new Value[valList.size()]);
                    userManager.setProtectedProperty(node, P_GROUPS, values);
                }
                return true;
            } catch (RepositoryException e) {
                // modification failed -> revert all pending changes.
                node.refresh(false);
                throw e;
            }
        } else {
            // nothing changed
            log.debug(message);
            return false;
        }
    }

    private void collectMembership(List groups, boolean includedIndirect) throws RepositoryException {
        NodeImpl node = getNode();
        if (!node.hasProperty(P_GROUPS)) {
            return;
        }
        Value[] refs = node.getProperty(P_GROUPS).getValues();
        for (int i = 0; i < refs.length; i++) {
            try {
                NodeImpl groupNode = (NodeImpl) getSession().getNodeByUUID(refs[i].getString());
                Group group = GroupImpl.create(groupNode, userManager);
                if (groups.add(group) && includedIndirect) {
                    ((AuthorizableImpl) group).collectMembership(groups, true);
                }
            } catch (ItemNotFoundException e) {
                // groupNode doesn't exist any more
                log.warn("Group node referenced by " + getID() + " doesn't exist -> Ignored from membership list.");
                // TODO: ev. clean up list of group memberships
            }
        }
    }

    /**
     * Returns true if the given property of the authorizable node is one of the
     * non-protected properties defined by the rep:authorizable.
     *
     * @param prop Property to be tested.
     * @return <code>true</code> if the given property is defined
     * by the rep:authorizable node type or one of it's sub-node types;
     * <code>false</code> otherwise.
     * @throws RepositoryException If the property definition cannot be retrieved.
     */
    private static boolean isAuthorizableProperty(Property prop) throws RepositoryException {
        PropertyDefinition def = prop.getDefinition();
        if (def.isProtected()) {
            return false;
        } else  {
            NodeTypeImpl declaringNt = (NodeTypeImpl) prop.getDefinition().getDeclaringNodeType();
            return declaringNt.isNodeType(UserConstants.NT_REP_AUTHORIZABLE);
        }
    }

    /**
     * Test if the JCR property to be modified/removed is one of the
     * following that has a special meaning and must be altered using this
     * user API:
     * <ul>
     * <ul>
     * <li>rep:principalName</li>
     * <li>rep:userId</li>
     * <li>rep:referees</li>
     * <li>rep:groups</li>
     * <li>rep:impersonators</li>
     * <li>rep:password</li>
     * </ul>
     * Those properties are 'protected' in their property definition. This
     * method is a simple utility in order to save the extra effort to modify
     * the props just to find out later that they are in fact protected.
     *
     * @param propertyName Name of the property.
     * @return true if the property with the given name represents a protected
     * user/group property that needs to be changed through the API.
     * @throws RepositoryException If the specified name is not valid.
     */
    private boolean isProtectedProperty(String propertyName) throws RepositoryException {
        Name pName = getSession().getQName(propertyName);
        return P_PRINCIPAL_NAME.equals(pName) || P_USERID.equals(pName)
                || P_REFEREES.equals(pName) || P_GROUPS.equals(pName)
                || P_IMPERSONATORS.equals(pName) || P_PASSWORD.equals(pName);
    }

    /**
     * Throws ConstraintViolationException if {@link #isProtectedProperty(String)}
     * returns <code>true</code>.
     *
     * @param propertyName Name of the property.
     * @throws ConstraintViolationException If the property is protected according
     * to {@link #isProtectedProperty(String)}.
     * @throws RepositoryException If another error occurs.
     */
    private void checkProtectedProperty(String propertyName) throws ConstraintViolationException, RepositoryException {
        if (isProtectedProperty(propertyName)) {
            throw new ConstraintViolationException("Attempt to modify protected property " + propertyName + " of an Authorizable.");
        }
    }

    private List getRefereeValues() throws RepositoryException {
        List principalNames = new ArrayList();
        if (node.hasProperty(P_REFEREES)) {
            try {
                Value[] refProp = node.getProperty(P_REFEREES).getValues();
                for (int i = 0; i < refProp.length; i++) {
                    principalNames.add(refProp[i]);
                }
            } catch (PathNotFoundException e) {
                // ignore. should never occur.
            }
        }
        return principalNames;
    }

    //--------------------------------------------------------------------------
    /**
     *
     */
    class NodeBasedPrincipal extends PrincipalImpl implements ItemBasedPrincipal {

        /**
         * @param name for the principal
         */
        NodeBasedPrincipal(String name) {
            super(name);
        }

        //---------------------------------------------< ItemBasedPrincipal >---
        /**
         * Method revealing the path to the Node that represents the
         * Authorizable this principal is created for.
         *
         * @return The path of the underlying node.
         * @see ItemBasedPrincipal#getPath()
         */
        public String getPath() throws RepositoryException {
            return node.getPath();
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.security.user.AuthorizableImpl$NodeBasedPrincipal

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.