blob: 7e4418682daeba7f60458747ed056854f44e794d [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 com.google.common.base.Stopwatch;
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.iterator.RangeIteratorAdapter;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.plugins.tree.TreeAware;
import org.apache.jackrabbit.oak.security.user.monitor.UserMonitor;
import org.apache.jackrabbit.oak.spi.security.user.AuthorizableType;
import org.apache.jackrabbit.oak.spi.security.user.DynamicMembershipProvider;
import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import java.util.Collections;
import java.util.Iterator;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static org.apache.jackrabbit.oak.api.Type.STRING;
/**
* Base class for {@code User} and {@code Group} implementations.
*/
abstract class AuthorizableImpl implements Authorizable, UserConstants, TreeAware {
/**
* logger instance
*/
private static final Logger log = LoggerFactory.getLogger(AuthorizableImpl.class);
private final String id;
private final Tree tree;
private final UserManagerImpl userManager;
private String principalName;
private AuthorizableProperties properties;
private int hashCode;
AuthorizableImpl(@NotNull String id, @NotNull Tree tree,
@NotNull UserManagerImpl userManager) throws RepositoryException {
checkValidTree(tree);
this.id = id;
this.tree = tree;
this.userManager = userManager;
}
abstract void checkValidTree(@NotNull Tree tree) throws RepositoryException;
static boolean isValidAuthorizableImpl(@NotNull Authorizable authorizable) {
return authorizable instanceof AuthorizableImpl;
}
//-------------------------------------------------------< Authorizable >---
@NotNull
@Override
public String getID() {
return id;
}
@NotNull
@Override
public Iterator<Group> declaredMemberOf() throws RepositoryException {
return memberOfMonitored(false);
}
@NotNull
@Override
public Iterator<Group> memberOf() throws RepositoryException {
return memberOfMonitored(true);
}
@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.");
}
userManager.onRemove(this);
getTree().remove();
}
@NotNull
@Override
public Iterator<String> getPropertyNames() throws RepositoryException {
return getPropertyNames(".");
}
@NotNull
@Override
public Iterator<String> getPropertyNames(@NotNull String relPath) throws RepositoryException {
return getAuthorizableProperties().getNames(relPath);
}
@Override
public boolean hasProperty(@NotNull String relPath) throws RepositoryException {
return getAuthorizableProperties().hasProperty(relPath);
}
@Nullable
@Override
public Value[] getProperty(@NotNull String relPath) throws RepositoryException {
return getAuthorizableProperties().getProperty(relPath);
}
@Override
public void setProperty(@NotNull String relPath, @Nullable Value value) throws RepositoryException {
getAuthorizableProperties().setProperty(relPath, value);
}
@Override
public void setProperty(@NotNull String relPath, @Nullable Value[] values) throws RepositoryException {
getAuthorizableProperties().setProperty(relPath, values);
}
@Override
public boolean removeProperty(@NotNull String relPath) throws RepositoryException {
return getAuthorizableProperties().removeProperty(relPath);
}
@NotNull
@Override
public String getPath() {
return userManager.getNamePathMapper().getJcrPath(getTree().getPath());
}
//-------------------------------------------------------------< Object >---
@Override
public int hashCode() {
if (hashCode == 0) {
StringBuilder sb = new StringBuilder();
sb.append(isGroup() ? "group:" : "user:");
sb.append(':');
sb.append(id);
sb.append('_');
sb.append(userManager.hashCode());
hashCode = sb.toString().hashCode();
}
return hashCode;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof AuthorizableImpl) {
AuthorizableImpl otherAuth = (AuthorizableImpl) obj;
return isGroup() == otherAuth.isGroup() && id.equals(otherAuth.id) && userManager.equals(otherAuth.userManager);
}
return false;
}
@Override
public String toString() {
String typeStr = (isGroup()) ? "Group '" : "User '";
return new StringBuilder().append(typeStr).append(id).append('\'').toString();
}
//----------------------------------------------------------< TreeAware >---
@Override
@NotNull
public Tree getTree() {
if (tree.exists()) {
return tree;
} else {
throw new IllegalStateException("Authorizable " + id + ": underlying tree has been disconnected.");
}
}
//--------------------------------------------------------------------------
@Nullable
String getPrincipalNameOrNull() {
if (principalName == null) {
PropertyState pNameProp = tree.getProperty(REP_PRINCIPAL_NAME);
if (pNameProp != null) {
principalName = pNameProp.getValue(STRING);
}
}
return principalName;
}
@NotNull
String getPrincipalName() throws RepositoryException {
String pName = getPrincipalNameOrNull();
if (pName == null) {
String msg = "Authorizable without principal name " + id;
log.warn(msg);
throw new RepositoryException(msg);
}
return pName;
}
/**
* @return The user manager associated with this authorizable.
*/
@NotNull
UserManagerImpl getUserManager() {
return userManager;
}
/**
* @return The membership provider associated with this authorizable
*/
@NotNull
MembershipProvider getMembershipProvider() {
return userManager.getMembershipProvider();
}
@NotNull
UserMonitor getMonitor() {
return userManager.getMonitor();
}
/**
* Returns {@code true} if this authorizable represents the 'everyone' group.
*
* @return {@code true} if this authorizable represents the group everyone
* is member of; {@code false} otherwise.
*/
boolean isEveryone() {
return Utils.isEveryone(this);
}
/**
* Retrieve authorizable properties for property related operations.
*
* @return The authorizable properties for this user/group.
*/
private AuthorizableProperties getAuthorizableProperties() {
if (properties == null) {
properties = new AuthorizablePropertiesImpl(this, userManager.getPartialValueFactory());
}
return properties;
}
@NotNull
private Iterator<Group> memberOfMonitored(boolean includeInherited) throws RepositoryException {
Stopwatch watch = Stopwatch.createStarted();
Iterator<Group> groups = getMembership(includeInherited);
getMonitor().doneMemberOf(watch.elapsed(NANOSECONDS), !includeInherited);
return groups;
}
/**
* Retrieve the group membership of this authorizable.
*
* @param includeInherited Flag indicating whether the resulting iterator only
* contains groups this authorizable is declared member of or if inherited
* group membership is respected.
*
* @return Iterator of groups this authorizable is (declared) member of.
* @throws RepositoryException If an error occurs.
*/
@NotNull
private Iterator<Group> getMembership(boolean includeInherited) throws RepositoryException {
if (isEveryone()) {
return Collections.emptyIterator();
}
DynamicMembershipProvider dmp = userManager.getDynamicMembershipProvider();
Iterator<Group> dynamicGroups = dmp.getMembership(this, includeInherited);
MembershipProvider mMgr = getMembershipProvider();
Iterator<Tree> trees = mMgr.getMembership(getTree(), includeInherited);
if (!trees.hasNext()) {
return dynamicGroups;
}
AuthorizableIterator groups = AuthorizableIterator.create(trees, userManager, AuthorizableType.GROUP);
AuthorizableIterator allGroups = AuthorizableIterator.create(true, dynamicGroups, groups);
return new RangeIteratorAdapter(allGroups);
}
}