/* 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.io.Serializable;
import diskCacheV111.util.FsPath;
/**
* A Restriction provides an overlay authorisation layer where some actions of
* that a user would normally enjoy are denied; i.e., a Restriction may only
* reduce what a user is able to achieve should the user's requests be
* considered separately from any Restriction.
* <p>
* The main use-case is to allow delegation with reduced authorisation.
* Delegation is where one agent (that is authorised to use dCache) somehow
* allows another agent (that is otherwise not allowed to use dCache) to access
* dCache according to the first agent's authorisation. This is commonly
* referred to as the second agent "acting on behalf of" the first agent.
* Delegation is often achieved by the first agent providing some credential to
* the second agent. Ideally, the credential passed to the second agent is
* limited as much as possible, to reduce misuse should the second agent be
* untrustworthy or the credential is stolen. When logging in, the second
* agent's delegated credential would attract a Restriction that limits what is
* authorised.
* <p>
* Here is a mapping between common operations and the corresponding Restriction
* checks:
* <dl>
* <dt>Change current directory</dt>
* <dd>
* Requires {@code Activity.READ_METADATA} on the new path.
* </dd>
*
* <dt>List contents of a directory</dt>
* <dd>
* Requires {@code Activity.LIST} on the directory path. Each child
* of the directory that does not have {@code Activity.READ_METADATA}
* is excluded from the list.
* </dd>
*
* <dt>Read information about a file or directory</dt>
* <dd>
* Requires {@code Activity.READ_METADATA} on the path.
* </dd>
*
* <dt>Write a new file</dt>
* <dd>
* Requires {@code Activity.UPLOAD} on the new file's path.
* </dd>
*
* <dt>Delete a file or directory</dt>
* <dd>
* Requires {@code Activity.DELETE} on the path.
* </dd>
*
* <dt>Rename or move a file</dt>
* <dd>
* Requires {@code Activity.MANAGE} on source's and target's parent
* directories. If the move would overwrite an existing file then
* {@code Activity.DELETE} is also needed on the target path.
* </dd>
*
* <dt>Create a symbolic link</dt>
* <dd>
* Requires {@code Activity.MANAGE} on the symbolic link's parent
* directory.
* </dd>
*
* <dt>Creating an internal copy of a file</dt>
* <dd>
* Requires {@code Activity.DOWNLOAD} on source file and
* {@code Activity.UPLOAD} on the target directory.
* </dd>
* </dl>
* <p>
* Restrictions should be written with a "No Islands" rule in mind.
* Specifically, all Restrictions should be written such that, if a path has no
* restriction for some activity then the parent path has no restriction for
* {@code Activity.READ_METADATA}. The consequence is that, when checking
* permissions, it is safe to check only the longest (or most specific) path
* against the user's activity.
* <p>
* Restrictions can form a subsumption hierarchy. A restriction A is said to
* subsume a restriction B if a denied operations in A are also denied by B.
* Intuitively, B subsumes A if B is at least as strict as A.
*/
public interface Restriction extends LoginAttribute, Serializable
{
/**
* Discover if the user is restricted for this activity and path.
* @param activity What the user is attempting.
* @param path absolute path within dCache of the namespace entry affected.
* @return true if the user is not allowed this activity.
*/
boolean isRestricted(Activity activity, FsPath path);
/**
* An optimised version of isRestricted. A restriction must respond as
* if {@literal isRestricted(activity, new FsPath(directory).add(child));}
* were called, but the method may be able to avoid creating a new FsPath
* object.
* @param activity What the user is attempting.
* @param directory The directory containing the target
* @param child The name of the target object within directory.
* @return true if the user is not allowed this activity.
*/
boolean isRestricted(Activity activity, FsPath directory, String child);
/**
* Whether another object is an equivalent restriction.
* @param other The object to compare
* @return true iff {@literal other} implements {@literal Restriction}
* and {@code #isRestricted} and {@literal other.isRestricted} return the
* same value for all (activity,path) pairs.
*/
@Override
boolean equals(Object other);
/**
* Check whether {@literal other} denies all operations that this
* restriction denies. This is a transitive relationship. Note that if
* {@literal A.isSubsumedBy(B)} and {@literal B.isSubsumedBy(A)} then
* A and B are equivalence. Two restrictions that are equal are always
* equivalent; however, there may be pairs of equivalent restrictions that
* are not equal. If a class cannot determine whether it is subsumed by
* some other class then it should return false.
* @param other The Restriction to compare.
* @return true if {@code other#isRestricted} returns true for all
* (activity,path) pairs that {@code #isRestricted} returns true.
*/
boolean isSubsumedBy(Restriction other);
}