blob: cafc130011a2ff9d92ef740b2c98d88a0b7e1c72 [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.privilege;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.jcr.InvalidItemStateException;
import javax.jcr.RepositoryException;
import javax.jcr.security.AccessControlException;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition;
import org.apache.jackrabbit.oak.spi.security.privilege.ImmutablePrivilegeDefinition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@code PrivilegeManager} implementation reading from and storing privileges
* into the repository.
*/
class PrivilegeManagerImpl implements PrivilegeManager {
private static final Logger log = LoggerFactory.getLogger(PrivilegeManagerImpl.class);
private final Root root;
private final NamePathMapper namePathMapper;
PrivilegeManagerImpl(Root root, NamePathMapper namePathMapper) {
this.root = root;
this.namePathMapper = namePathMapper;
}
//---------------------------------------------------< PrivilegeManager >---
@Override
public Privilege[] getRegisteredPrivileges() throws RepositoryException {
Set<Privilege> privileges = new HashSet();
for (PrivilegeDefinition def : getPrivilegeDefinitions()) {
privileges.add(getPrivilege(def));
}
return privileges.toArray(new Privilege[privileges.size()]);
}
@Override
public Privilege getPrivilege(String privilegeName) throws RepositoryException {
PrivilegeDefinition def = getPrivilegeDefinition(getOakName(privilegeName));
if (def == null) {
throw new AccessControlException("No such privilege " + privilegeName);
} else {
return getPrivilege(def);
}
}
@Override
public Privilege registerPrivilege(String privilegeName, boolean isAbstract,
String[] declaredAggregateNames) throws RepositoryException {
if (root.hasPendingChanges()) {
throw new InvalidItemStateException("Attempt to register a new privilege while there are pending changes.");
}
if (privilegeName == null || privilegeName.isEmpty()) {
throw new RepositoryException("Invalid privilege name " + privilegeName);
}
PrivilegeDefinition definition = new ImmutablePrivilegeDefinition(getOakName(privilegeName), isAbstract, getOakNames(declaredAggregateNames));
PrivilegeDefinitionWriter writer = new PrivilegeDefinitionWriter(getWriteRoot());
writer.writeDefinition(definition);
// refresh the current root to make sure the definition is visible
root.refresh();
return getPrivilege(definition);
}
//------------------------------------------------------------< private >---
@NotNull
private Root getWriteRoot() {
return root.getContentSession().getLatestRoot();
}
@NotNull
private Set<String> getOakNames(@Nullable String[] jcrNames) throws RepositoryException {
Set<String> oakNames;
if (jcrNames == null || jcrNames.length == 0) {
oakNames = Collections.emptySet();
} else {
oakNames = new HashSet(jcrNames.length);
for (String jcrName : jcrNames) {
String oakName = getOakName(jcrName);
oakNames.add(oakName);
}
}
return oakNames;
}
@NotNull
private String getOakName(@Nullable String jcrName) throws RepositoryException {
if (jcrName == null) {
throw new AccessControlException("Invalid privilege name 'null'");
}
String oakName = namePathMapper.getOakNameOrNull(jcrName);
if (oakName == null) {
throw new AccessControlException("Cannot resolve privilege name " + jcrName);
}
return oakName;
}
@NotNull
private Privilege getPrivilege(@NotNull PrivilegeDefinition definition) {
return new PrivilegeImpl(definition);
}
@NotNull
private PrivilegeDefinition[] getPrivilegeDefinitions() {
Map<String, PrivilegeDefinition> definitions = getReader().readDefinitions();
return definitions.values().toArray(new PrivilegeDefinition[definitions.size()]);
}
@Nullable
private PrivilegeDefinition getPrivilegeDefinition(@NotNull String oakName) {
return getReader().readDefinition(oakName);
}
@NotNull
private PrivilegeDefinitionReader getReader() {
return new PrivilegeDefinitionReader(root);
}
//--------------------------------------------------------------------------
/**
* Privilege implementation based on a {@link org.apache.jackrabbit.oak.spi.security.privilege.PrivilegeDefinition}.
*/
private final class PrivilegeImpl implements Privilege {
private final PrivilegeDefinition definition;
private PrivilegeImpl(@NotNull PrivilegeDefinition definition) {
this.definition = definition;
}
//------------------------------------------------------< Privilege >---
@Override
public String getName() {
return namePathMapper.getJcrName(definition.getName());
}
@Override
public boolean isAbstract() {
return definition.isAbstract();
}
@Override
public boolean isAggregate() {
return !definition.getDeclaredAggregateNames().isEmpty();
}
@Override
public Privilege[] getDeclaredAggregatePrivileges() {
Set<String> declaredAggregateNames = definition.getDeclaredAggregateNames();
Set<Privilege> declaredAggregates = new HashSet(declaredAggregateNames.size());
for (String oakName : declaredAggregateNames) {
if (oakName.equals(definition.getName())) {
log.warn("Found cyclic privilege aggregation -> ignore declared aggregate " + oakName);
continue;
}
PrivilegeDefinition def = getPrivilegeDefinition(oakName);
if (def != null) {
declaredAggregates.add(getPrivilege(def));
} else {
log.warn("Invalid privilege '{}' in declared aggregates of '{}'", oakName, getName());
}
}
return declaredAggregates.toArray(new Privilege[declaredAggregates.size()]);
}
@Override
public Privilege[] getAggregatePrivileges() {
Set<Privilege> aggr = new HashSet();
for (Privilege decl : getDeclaredAggregatePrivileges()) {
aggr.add(decl);
if (decl.isAggregate()) {
aggr.addAll(Arrays.asList(decl.getAggregatePrivileges()));
}
}
return aggr.toArray(new Privilege[aggr.size()]);
}
//---------------------------------------------------------< Object >---
@Override
public int hashCode() {
return definition.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (o instanceof PrivilegeImpl) {
return definition.equals(((PrivilegeImpl) o).definition);
} else {
return false;
}
}
@Override
public String toString() {
return definition.getName();
}
}
}