/* dCache - http://www.dcache.org/
*
* Copyright (C) 2015 Deutsches Elektronen-Synchrotron
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.dcache.auth.attributes;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import diskCacheV111.util.FsPath;
import static org.dcache.auth.attributes.Activity.*;
import static java.util.Arrays.asList;
/**
* Class containing utility methods.
*/
public class Restrictions
{
private static final Restriction UNRESTRICTED = DenyActivityRestriction.restrictNoActivity();
private static final Restriction DENY_ALL = DenyActivityRestriction.restrictAllActivity();
private static final Restriction READ_ONLY = new DenyActivityRestriction(DELETE, MANAGE, UPDATE_METADATA, UPLOAD);
private Restrictions()
{
// prevent instantiating.
}
public static Restriction denyAll()
{
return DENY_ALL;
}
public static Restriction none()
{
return UNRESTRICTED;
}
public static Restriction readOnly()
{
return READ_ONLY;
}
/**
* Compose multiple restrictions where each Restriction can veto an
* operation.
*/
public static Restriction concat(Restriction... restrictions)
{
switch (restrictions.length) {
case 0:
return UNRESTRICTED;
case 1:
return restrictions[0];
case 2:
if (restrictions [0].isSubsumedBy(restrictions [1])) {
return restrictions [1];
}
if (restrictions [1].isSubsumedBy(restrictions [0])) {
return restrictions [0];
}
// Fall through for default behaviour
default:
return concat(asList(restrictions));
}
}
/**
* Compose multiple Restrictions where each Restriction can veto an
* operation.
*/
public static Restriction concat(Iterable<Restriction> restrictions)
{
CompositeRestrictionBuilder composer = new CompositeRestrictionBuilder();
for (Restriction r : restrictions) {
if (r instanceof CompositeRestriction) {
for (Restriction inner : ((CompositeRestriction) r).restrictions) {
composer.accept(inner);
}
} else {
composer.accept(r);
}
}
return composer.build();
}
/**
* Build an optimised composition of restrictions.
*/
private static class CompositeRestrictionBuilder
{
private final Set<Restriction> restrictions = new HashSet<>();
private void accept(Restriction newRestriction)
{
Iterator<Restriction> i = restrictions.iterator();
while (i.hasNext()) {
Restriction existing = i.next();
if (newRestriction.isSubsumedBy(existing)) {
return;
}
if (existing.isSubsumedBy(newRestriction)) {
i.remove();
}
}
restrictions.add(newRestriction);
}
private Restriction build()
{
switch (restrictions.size()) {
case 0:
return UNRESTRICTED;
case 1:
return restrictions.iterator().next();
default:
return new CompositeRestriction(restrictions);
}
}
}
/**
* A composite of multiple restrictions where any restriction can veto
* an activity.
*/
private static class CompositeRestriction implements Restriction
{
private static final long serialVersionUID = 1854305439062458336L;
private final Set<Restriction> restrictions;
public CompositeRestriction(Set<Restriction> restrictions)
{
this.restrictions = restrictions;
}
@Override
public boolean isRestricted(Activity activity, FsPath path)
{
for (Restriction r : restrictions) {
if (r.isRestricted(activity, path)) {
return true;
}
}
return false;
}
@Override
public boolean isRestricted(Activity activity, FsPath directory, String name)
{
for (Restriction r : restrictions) {
if (r.isRestricted(activity, directory, name)) {
return true;
}
}
return false;
}
@Override
public boolean equals(Object other)
{
if (!(other instanceof CompositeRestriction)) {
return false;
}
CompositeRestriction r = (CompositeRestriction) other;
return restrictions.equals(r.restrictions);
}
@Override
public int hashCode()
{
return Objects.hashCode(restrictions);
}
@Override
public boolean isSubsumedBy(Restriction other)
{
if (other instanceof CompositeRestriction) {
return isSubsumedBy((CompositeRestriction) other);
}
for (Restriction r : restrictions) {
if (!r.isSubsumedBy(other)) {
return false;
}
}
return true;
}
private boolean isSubsumedBy(CompositeRestriction other)
{
for (Restriction r : restrictions) {
if (!other.subsumes(r)) {
return false;
}
}
return true;
}
private boolean subsumes(Restriction other)
{
for (Restriction r : restrictions) {
if (other.isSubsumedBy(r)) {
return true;
}
}
return false;
}
}
}